├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-request.md ├── dependabot.yml └── workflows │ ├── action-semantic-pull-request.yml │ ├── greetings.yml │ ├── nightly.yml │ └── trunk_check.yml ├── .gitignore ├── .trunk ├── .gitignore ├── configs │ ├── .clang-format │ ├── .flake8 │ ├── .hadolint.yaml │ ├── .isort.cfg │ ├── .markdownlint.yaml │ ├── .prettierrc.yaml │ ├── .shellcheckrc │ ├── .yamllint.yaml │ ├── ruff.toml │ └── svgo.config.js └── trunk.yaml ├── .vscode ├── extensions.json └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── Doctest.cmake ├── LVGL.cmake ├── LovyanGFX.cmake ├── Portduino.cmake ├── esp32_toolchain.cmake ├── nanopb.cmake └── protobuf.cmake ├── docs ├── CYD.png ├── Pi400-TFT.jpg ├── T-Deck.jpg ├── X11.png ├── class-diagram.png └── tile_pyramid.png ├── drivers ├── Touch_CST820.cpp └── Touch_CST820.h ├── extra_script.py ├── generated ├── ui_128x32 │ └── README.md ├── ui_128x64 │ └── README.md ├── ui_160x80 │ └── README.md ├── ui_240x240 │ └── README.md └── ui_320x240 │ ├── README.md │ ├── actions.h │ ├── fonts.h │ ├── images.c │ ├── images.h │ ├── screens.c │ ├── screens.h │ ├── styles.c │ ├── styles.h │ ├── ui.c │ ├── ui.h │ ├── ui_font_montserrat_12.c │ ├── ui_font_montserrat_14.c │ ├── ui_font_montserrat_16.c │ ├── ui_font_montserrat_20.c │ ├── ui_image_battery_bolt_image.c │ ├── ui_image_battery_empty_image.c │ ├── ui_image_battery_empty_warn_image.c │ ├── ui_image_battery_full_image.c │ ├── ui_image_battery_low_image.c │ ├── ui_image_battery_mid_image.c │ ├── ui_image_battery_plug_image.c │ ├── ui_image_battery_slash_image.c │ ├── ui_image_bluetooth_on_image.c │ ├── ui_image_circle_image.c │ ├── ui_image_circle_question_image.c │ ├── ui_image_debug_log_image.c │ ├── ui_image_gps_location_image.c │ ├── ui_image_groups_button_image.c │ ├── ui_image_groups_key_image.c │ ├── ui_image_groups_lock_image.c │ ├── ui_image_groups_unlock_image.c │ ├── ui_image_home_bell_image.c │ ├── ui_image_home_bell_slash_image.c │ ├── ui_image_home_bluetooth_off_button_image.c │ ├── ui_image_home_bluetooth_on_button_image.c │ ├── ui_image_home_button_image.c │ ├── ui_image_home_clock_image.c │ ├── ui_image_home_ethernet_button_image.c │ ├── ui_image_home_fair_signal_image.c │ ├── ui_image_home_good_signal_image.c │ ├── ui_image_home_location_button_image.c │ ├── ui_image_home_lora_image.c │ ├── ui_image_home_mail_button_image.c │ ├── ui_image_home_mail_unread_button_image.c │ ├── ui_image_home_memory_button.c │ ├── ui_image_home_no_signal_image.c │ ├── ui_image_home_nodes_button_image.c │ ├── ui_image_home_sd_card_image.c │ ├── ui_image_home_signal_button_image.c │ ├── ui_image_home_strong_signal_image.c │ ├── ui_image_home_weak_signal_image.c │ ├── ui_image_home_wlan_button_image.c │ ├── ui_image_home_wlan_off_image.c │ ├── ui_image_hourgalss_start_image.c │ ├── ui_image_img_no_tile_image.c │ ├── ui_image_key_generate_image.c │ ├── ui_image_keyboard_image.c │ ├── ui_image_knob_logo_image.c │ ├── ui_image_location_crosshairs_18.c │ ├── ui_image_location_image.c │ ├── ui_image_location_lock_image.c │ ├── ui_image_location_pin_14_image.c │ ├── ui_image_location_pin_image.c │ ├── ui_image_lock_channel_image.c │ ├── ui_image_lock_secure_image.c │ ├── ui_image_lock_slash_image.c │ ├── ui_image_map_button_image.c │ ├── ui_image_map_file_block_image.c │ ├── ui_image_meshtastic_boot_logo_image.c │ ├── ui_image_meshtastic_logo_image.c │ ├── ui_image_messages_button_image.c │ ├── ui_image_mqtt_logo2_image.c │ ├── ui_image_msg_popup_button_image.c │ ├── ui_image_nav_down_image.c │ ├── ui_image_nav_home_image.c │ ├── ui_image_nav_left_image.c │ ├── ui_image_nav_right_image.c │ ├── ui_image_nav_up_image.c │ ├── ui_image_no_tile_image.c │ ├── ui_image_node_client_image.c │ ├── ui_image_node_location_pin24_image.c │ ├── ui_image_node_location_pin_image.c │ ├── ui_image_node_router_image.c │ ├── ui_image_node_sensor_image.c │ ├── ui_image_nodes_button_image.c │ ├── ui_image_radar_beam_image.c │ ├── ui_image_reboot_bt_on_image.c │ ├── ui_image_reboot_image.c │ ├── ui_image_sd_card_image.c │ ├── ui_image_settings_button_image.c │ ├── ui_image_settings_trash_image.c │ ├── ui_image_shutdown_image.c │ ├── ui_image_signal_full_image.c │ ├── ui_image_signal_slider_image.c │ ├── ui_image_top_chart_image.c │ ├── ui_image_top_chats_image.c │ ├── ui_image_top_group_image.c │ ├── ui_image_top_loop_image.c │ ├── ui_image_top_lora_image.c │ ├── ui_image_top_map_image.c │ ├── ui_image_top_message_node_images.c │ ├── ui_image_top_neighbors_image.c │ ├── ui_image_top_node_detector_image.c │ ├── ui_image_top_nodes_image.c │ ├── ui_image_top_settings_image.c │ ├── ui_image_unmessagable_image.c │ ├── ui_image_user_question_image.c │ ├── ui_image_worldmap_image.c │ ├── ui_image_zoom_minus_image.c │ ├── ui_image_zoom_plus_image.c │ ├── ui_tabview_settings.c │ └── vars.h ├── include ├── comms │ ├── BluetoothClient.h │ ├── EthClient.h │ ├── IClientBase.h │ ├── MeshEnvelope.h │ ├── PacketClient.h │ ├── PacketServer.h │ ├── SerialClient.h │ ├── UARTClient.h │ └── WLANClient.h ├── graphics │ ├── DeviceGUI.h │ ├── DeviceScreen.h │ ├── LGFX │ │ ├── LGFXConfig.h │ │ ├── LGFX_4848S040.h │ │ ├── LGFX_ELECROW70.h │ │ ├── LGFX_ESP2432S022.h │ │ ├── LGFX_ESP2432S028RV1.h │ │ ├── LGFX_ESP2432S028RV2.h │ │ ├── LGFX_ESPILI9341XPT2046.h │ │ ├── LGFX_GENERIC.h │ │ ├── LGFX_HELTEC_TRACKER.h │ │ ├── LGFX_INDICATOR.h │ │ ├── LGFX_MAKERFABS480X480.h │ │ ├── LGFX_PICOMPUTER_S3.h │ │ ├── LGFX_PICO_TFT_240x135.h │ │ ├── LGFX_T_DECK.h │ │ ├── LGFX_T_HMI.h │ │ ├── LGFX_T_WATCH_S3.h │ │ ├── LGFX_UNPHONE.h │ │ └── LGFX_WT_SC01PLUS.h │ ├── LVGL │ │ └── LVGLGraphics.h │ ├── common │ │ ├── BatteryLevel.h │ │ ├── LoRaPresets.h │ │ ├── MeshtasticView.h │ │ ├── ResponseHandler.h │ │ ├── Ringtones.h │ │ ├── SdCard.h │ │ ├── ViewController.h │ │ └── ViewFactory.h │ ├── driver │ │ ├── DisplayDriver.h │ │ ├── DisplayDriverConfig.h │ │ ├── DisplayDriverFactory.h │ │ ├── FBDriver.h │ │ ├── LGFXDriver.h │ │ ├── OLEDDriver.h │ │ ├── TFTDriver.h │ │ └── X11Driver.h │ ├── map │ │ ├── FileSystemService.h │ │ ├── GeoPoint.h │ │ ├── LinuxFileSystemService.h │ │ ├── MapPanel.h │ │ ├── MapTile.h │ │ ├── MapTileSettings.h │ │ ├── OSMTiles.h │ │ ├── SDCardService.h │ │ ├── SdFatService.h │ │ ├── TileService.h │ │ └── URLService.h │ └── view │ │ ├── OLED │ │ ├── OLEDViewController.h │ │ └── OLEDView_128x64.h │ │ └── TFT │ │ ├── TFTView_160x80.h │ │ ├── TFTView_240x240.h │ │ ├── TFTView_320x240.h │ │ ├── TFTView_480x320.h │ │ └── Themes.h ├── input │ ├── ButtonInputDriver.h │ ├── EncoderInputDriver.h │ ├── I2CKeyboardInputDriver.h │ ├── InputDriver.h │ ├── KeyMatrixInputDriver.h │ └── LinuxInputDriver.h ├── lv_conf.h └── util │ ├── FileLoader.h │ ├── ILog.h │ ├── ILogEntry.h │ ├── LinuxHelper.h │ ├── LogMessage.h │ ├── LogRotate.h │ ├── Packet.h │ ├── PacketQueue.h │ ├── README.md │ ├── SharedQueue.h │ └── macaron_Base64.h ├── library.json ├── locale ├── README.md ├── de.yml ├── el.yml ├── en.yml ├── es.yml ├── fi.yml ├── fr.yml ├── it.yml ├── lv_i18n.c ├── lv_i18n.h ├── nl.yml ├── no.yml ├── pl.yml ├── pt.yml ├── ro.yml ├── ru.yml ├── se.yml ├── sl.yml ├── sr.yml ├── tr.yml ├── uk.yml └── zh-CN.yml ├── maps ├── README.md ├── atlas.zip ├── dark-matter-brown.zip ├── osm.zip └── positron.zip ├── portduino ├── Light_PWM.cpp └── Light_PWM.h ├── resources └── mouse_cursor_icon.c ├── source ├── comms │ ├── ethernet │ │ └── EthClient.cpp │ ├── packet │ │ ├── PacketClient.cpp │ │ └── PacketServer.cpp │ └── serial │ │ ├── BluetoothClient.cpp │ │ ├── MeshEnvelope.cpp │ │ ├── SerialClient.cpp │ │ ├── UARTClient.cpp │ │ └── WLANClient.cpp ├── graphics │ ├── DeviceGUI.cpp │ ├── DeviceScreen.cpp │ ├── LVGL │ │ └── LVGLGraphics.cpp │ ├── OLED │ │ ├── OLEDViewController.cpp │ │ └── OLEDView_128x64.cpp │ ├── TFT │ │ ├── TFTView_160x80.cpp │ │ ├── TFTView_240x240.cpp │ │ ├── TFTView_320x240.cpp │ │ ├── TFTView_480x320.cpp │ │ └── Themes.cpp │ ├── common │ │ ├── BatteryLevel.cpp │ │ ├── LoRaPresets.cpp │ │ ├── MeshtasticView.cpp │ │ ├── ResponseHandler.cpp │ │ ├── Ringtones.cpp │ │ ├── SdCard.cpp │ │ ├── ViewController.cpp │ │ └── ViewFactory.cpp │ ├── driver │ │ ├── DisplayDriver.cpp │ │ ├── DisplayDriverConfig.cpp │ │ ├── DisplayDriverFactory.cpp │ │ ├── FBDriver.cpp │ │ └── X11Driver.cpp │ └── map │ │ ├── FileSystemService.cpp │ │ ├── LinuxFileSystemService.cpp │ │ ├── MapPanel.cpp │ │ ├── MapTile.cpp │ │ ├── MapTileSettings.cpp │ │ ├── SDCardService.cpp │ │ ├── SdFatService.cpp │ │ ├── TileService.cpp │ │ └── URLService.cpp ├── input │ ├── ButtonInputDriver.cpp │ ├── EncoderInputDriver.cpp │ ├── I2CKeyboardInputDriver.cpp │ ├── InputDriver.cpp │ ├── KeyMatrixInputDriver.cpp │ └── LinuxInputDriver.cpp └── util │ ├── FileLoader.cpp │ ├── ILog.cpp │ ├── LinuxHelper.cpp │ ├── LogRotate.cpp │ └── SharedQueue.cpp ├── src ├── lib.cpp ├── mesh-pb-constants.cpp └── mesh-pb-constants.h ├── studio └── 320x240 │ └── TFT320x240.eez-project ├── tests ├── test_GeoPoint.cpp ├── test_MapPanel.cpp └── test_main.cpp └── version.txt /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # trunk-ignore-all(trivy/DS026) 2 | # trunk-ignore-all(trivy/DS002) 3 | # trunk-ignore-all(checkov/CKV_DOCKER_2) 4 | # trunk-ignore-all(checkov/CKV_DOCKER_3) 5 | FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12 6 | 7 | # trunk-ignore(terrascan/AC_DOCKER_0002) 8 | # trunk-ignore(hadolint/DL3008) 9 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 10 | && apt-get -y install --no-install-recommends \ 11 | ca-certificates \ 12 | git \ 13 | wget \ 14 | zip \ 15 | && apt-get clean && rm -rf /var/lib/apt/lists/* 16 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. 2 | { 3 | "name": "Meshtastic Device-UI Dev", 4 | "build": { 5 | "dockerfile": "Dockerfile" 6 | }, 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | "features": { 10 | "ghcr.io/trunk-io/devcontainer-feature/trunk:1": {}, 11 | "ghcr.io/devcontainers/features/node:1": {} 12 | }, 13 | 14 | // Configure tool-specific properties. 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "Trunk.io", 19 | "ms-vscode.cpptools-extension-pack", 20 | "ms-azuretools.vscode-docker" 21 | ], 22 | "settings": { 23 | "editor.formatOnSave": true, 24 | "editor.defaultFormatter": "trunk.io", 25 | "trunk.enableWindows": true, 26 | "files.insertFinalNewline": false, 27 | "files.trimFinalNewlines": false, 28 | "cmake.configureOnOpen": false, 29 | "[cpp]": { 30 | "editor.defaultFormatter": "trunk.io" 31 | } 32 | } 33 | } 34 | }, 35 | 36 | "postCreateCommand": "npm install lv_i18n -g --no-fund" 37 | } 38 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mverch67 # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve MUI 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots / Photos** 24 | If applicable, add screenshots or photos to help explain your problem. 25 | 26 | **Device (please complete the following information):** 27 | - Type: [e.g. T-Deck Plus, SenseCAP Indicator V2] 28 | - Exact version [e.g. 2.6.0.b85d9f988] 29 | - Firmware download source [e.g. web-flasher] 30 | - Other attached HW [e.g. m100 GPS] 31 | 32 | **Additional logs** 33 | Provide a device debug log: Please use USB cable to connect your device to a computer, run a serial terminal (e.g. from web-flasher) and execute the function to reproduce to problem. Copy&paste the log here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots or photos about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: docker 4 | directory: /.devcontainer 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: github-actions 8 | directory: /.github/workflows 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/action-semantic-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Lint PR 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | - reopened 10 | 11 | permissions: 12 | pull-requests: read 13 | 14 | jobs: 15 | main: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: amannn/action-semantic-pull-request@v5 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | greeting: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/first-interaction@v1 15 | with: 16 | repo-token: ${{ secrets.GITHUB_TOKEN }} 17 | issue-message: Congratulations for your first issue 18 | pr-message: Congratulations for your first pull request 19 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Nightly 2 | on: 3 | schedule: 4 | - cron: 0 8 * * 1-5 # Nightly at 8am on weekdays 5 | workflow_dispatch: {} 6 | 7 | permissions: read-all 8 | 9 | jobs: 10 | trunk_upgrade: 11 | # See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades 12 | name: Trunk Upgrade (PR) 13 | runs-on: ubuntu-24.04 14 | permissions: 15 | contents: write # For trunk to create PRs 16 | pull-requests: write # For trunk to create PRs 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Trunk Upgrade 22 | uses: trunk-io/trunk-action/upgrade@v1 23 | with: 24 | base: master 25 | -------------------------------------------------------------------------------- /.github/workflows/trunk_check.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | on: 3 | pull_request: 4 | merge_group: 5 | concurrency: 6 | group: ${{ github.head_ref || github.run_id }} 7 | cancel-in-progress: true 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | trunk_check: 13 | name: Trunk Check Runner 14 | runs-on: ubuntu-24.04 15 | permissions: 16 | checks: write # For trunk to post annotations 17 | contents: read # For repo checkout 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Trunk Check 24 | uses: trunk-io/trunk-action@v1 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | studio 7 | build -------------------------------------------------------------------------------- /.trunk/.gitignore: -------------------------------------------------------------------------------- 1 | *out 2 | *logs 3 | *actions 4 | *notifications 5 | *tools 6 | plugins 7 | user_trunk.yaml 8 | user.yaml 9 | tmp 10 | -------------------------------------------------------------------------------- /.trunk/configs/.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | IndentWidth: 4 3 | ColumnLimit: 130 4 | PointerAlignment: Right 5 | BreakBeforeBraces: Linux 6 | AllowShortFunctionsOnASingleLine: Inline 7 | -------------------------------------------------------------------------------- /.trunk/configs/.flake8: -------------------------------------------------------------------------------- 1 | # Autoformatter friendly flake8 config (all formatting rules disabled) 2 | [flake8] 3 | extend-ignore = D1, D2, E1, E2, E3, E501, W1, W2, W3, W5 4 | -------------------------------------------------------------------------------- /.trunk/configs/.hadolint.yaml: -------------------------------------------------------------------------------- 1 | # Following source doesn't work in most setups 2 | ignored: 3 | - SC1090 4 | - SC1091 5 | -------------------------------------------------------------------------------- /.trunk/configs/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile=black 3 | -------------------------------------------------------------------------------- /.trunk/configs/.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # Autoformatter friendly markdownlint config (all formatting rules disabled) 2 | default: true 3 | blank_lines: false 4 | bullet: false 5 | html: false 6 | indentation: false 7 | line_length: false 8 | spaces: false 9 | url: false 10 | whitespace: false 11 | -------------------------------------------------------------------------------- /.trunk/configs/.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | trailingComma: es5 2 | tabWidth: 2 3 | semi: false 4 | singleQuote: true 5 | overrides: 6 | # trunk-ignore(yamllint/quoted-strings) 7 | - files: 'locale/*.yml' 8 | options: 9 | printWidth: 200 10 | -------------------------------------------------------------------------------- /.trunk/configs/.shellcheckrc: -------------------------------------------------------------------------------- 1 | enable=all 2 | source-path=SCRIPTDIR 3 | disable=SC2154 4 | disable=SC2248 5 | disable=SC2250 6 | 7 | # If you're having issues with shellcheck following source, disable the errors via: 8 | # disable=SC1090 9 | # disable=SC1091 10 | # -------------------------------------------------------------------------------- /.trunk/configs/.yamllint.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | quoted-strings: 3 | required: only-when-needed 4 | extra-allowed: ["{|}"] 5 | empty-values: 6 | forbid-in-block-mappings: false 7 | forbid-in-flow-mappings: true 8 | key-duplicates: {} 9 | octal-values: 10 | forbid-implicit-octal: true 11 | -------------------------------------------------------------------------------- /.trunk/configs/ruff.toml: -------------------------------------------------------------------------------- 1 | # Generic, formatter-friendly config. 2 | select = ["B", "D3", "D4", "E", "F"] 3 | 4 | # Never enforce `E501` (line length violations). This should be handled by formatters. 5 | ignore = ["E501"] 6 | -------------------------------------------------------------------------------- /.trunk/configs/svgo.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | { 4 | name: "preset-default", 5 | params: { 6 | overrides: { 7 | removeViewBox: false, // https://github.com/svg/svgo/issues/1128 8 | sortAttrs: true, 9 | removeOffCanvasPaths: true, 10 | }, 11 | }, 12 | }, 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /.trunk/trunk.yaml: -------------------------------------------------------------------------------- 1 | # This file controls the behavior of Trunk: https://docs.trunk.io/cli 2 | # To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml 3 | version: 0.1 4 | cli: 5 | version: 1.24.0 6 | plugins: 7 | sources: 8 | - id: trunk 9 | ref: v1.7.0 10 | uri: https://github.com/trunk-io/plugins 11 | # Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes) 12 | runtimes: 13 | enabled: 14 | - go@1.21.0 15 | - node@22.16.0 16 | - python@3.10.8 17 | # This is the section where you manage your linters. (https://docs.trunk.io/check/configuration) 18 | lint: 19 | enabled: 20 | - trufflehog@3.88.35 21 | - yamllint@1.37.1 22 | - bandit@1.8.3 23 | - checkov@3.2.436 24 | - terrascan@1.19.9 25 | - trivy@0.63.0 26 | #- trufflehog@3.63.2-rc0 27 | - taplo@0.9.3 28 | - ruff@0.11.12 29 | - isort@6.0.1 30 | - markdownlint@0.45.0 31 | - oxipng@9.1.5 32 | - svgo@3.3.2 33 | - actionlint@1.7.7 34 | - flake8@7.2.0 35 | - hadolint@2.12.1-beta 36 | - shfmt@3.6.0 37 | - shellcheck@0.10.0 38 | - black@25.1.0 39 | - git-diff-check 40 | - gitleaks@8.27.0 41 | - clang-format@16.0.3 42 | - prettier@3.5.3 43 | ignore: 44 | - linters: [ALL] 45 | paths: 46 | # Ignore generated files 47 | - generated/** 48 | # Ignore "special" platformio Python script 49 | - extra_script.py 50 | actions: 51 | disabled: 52 | - trunk-announce 53 | enabled: 54 | - trunk-fmt-pre-commit 55 | - trunk-check-pre-push 56 | - trunk-upgrade-available 57 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format. 4 | "recommendations": [ 5 | "Trunk.io", 6 | "matepek.vscode-catch2-test-adapter", 7 | "ms-vscode.cmake-tools", 8 | "twxs.cmake" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file 3 | // for the documentation about the settings.json format. 4 | "editor.formatOnSave": true, 5 | "editor.defaultFormatter": "trunk.io", 6 | "trunk.enableWindows": true, 7 | "files.insertFinalNewline": false, 8 | "files.trimFinalNewlines": false, 9 | "cmake.configureOnOpen": false, 10 | "[cpp]": { 11 | "editor.defaultFormatter": "trunk.io" 12 | }, 13 | "testMate.cpp.test.executables": "${workspaceFolder}/build/bin/tests", 14 | "testMate.cpp.discovery.loadOnStartup": true, 15 | "cmake.ctest.testExplorerIntegrationEnabled": false 16 | } 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | ################################################################################ 4 | ## D E V I C E U I # 5 | ################################################################################ 6 | file(READ ${CMAKE_CURRENT_SOURCE_DIR}/version.txt ver) 7 | project(device_ui VERSION ${ver} HOMEPAGE_URL https://github.com/meshtastic/device-ui LANGUAGES C CXX) 8 | 9 | message(STATUS "device-ui version: ${PROJECT_VERSION}") 10 | 11 | set(MAIN_PROJECT OFF) 12 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 13 | set(MAIN_PROJECT ON) 14 | endif() 15 | 16 | option(ENABLE_DOCTESTS "Include tests in the library. Setting this to OFF will remove all doctest related code. 17 | Tests in tests/*.cpp will still be enabled." ${MAIN_PROJECT}) 18 | option(ENABLE_DEBUG_LOG "Enable debug log" OFF) 19 | 20 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 21 | set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON) # with newer cmake versions put all find_package in global scope 22 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 23 | set(CMAKE_CXX_STANDARD 14) 24 | set(CMAKE_CXX_EXTENSIONS OFF) 25 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 26 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") 27 | set(GENERATED_VIEW ${VIEW} "ui_320x240") 28 | 29 | message(STATUS "Using C++${CMAKE_CXX_STANDARD}") 30 | message(STATUS "Using Generated View: " ${GENERATED_VIEW}) 31 | 32 | add_compile_definitions(ARCH_PORTDUINO) 33 | add_compile_definitions(ARDUINO) 34 | add_compile_definitions(VIEW_320x240) 35 | add_compile_definitions(USE_X11=1) 36 | 37 | include(FetchContent) 38 | include(Portduino) 39 | include(LovyanGFX) 40 | include(LVGL) 41 | include(protobuf) 42 | include(nanopb) 43 | include(Doctest) 44 | 45 | file(GLOB_RECURSE sources source/* generated/* portduino/* locale/* generated/${GENERATED_VIEW}/*) 46 | file(GLOB_RECURSE sources_test tests/*.cpp) 47 | 48 | add_library(DeviceUI ${sources}) 49 | target_link_libraries(DeviceUI PRIVATE lvgl::lvgl LovyanGFX Portduino Protobufs) 50 | target_compile_options(DeviceUI PUBLIC -Wall -Wno-format -Wfloat-conversion) 51 | 52 | target_include_directories(DeviceUI PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 53 | target_include_directories(DeviceUI PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) 54 | target_include_directories(DeviceUI PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/locale) 55 | target_include_directories(DeviceUI PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/portduino) 56 | target_include_directories(DeviceUI PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated/${GENERATED_VIEW}) 57 | target_include_directories(DeviceUI PRIVATE ${GENERATED_FILES_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${nanopb_SOURCE_DIR}) 58 | 59 | if(ENABLE_DOCTESTS) 60 | target_include_directories(DeviceUI PRIVATE ${doctest_SOURCE_DIR}) 61 | target_compile_definitions(DeviceUI PRIVATE UNIT_TEST) 62 | if(ENABLE_DEBUG_LOG) 63 | target_compile_definitions(DeviceUI PRIVATE DEBUG_UNIT_TEST) 64 | endif() 65 | endif() 66 | 67 | # 68 | # Unit Tests 69 | # 70 | if(ENABLE_DOCTESTS) 71 | include(CTest) 72 | enable_testing() 73 | add_executable(tests ${sources_test}) 74 | target_link_libraries(tests PRIVATE DeviceUI doctest::doctest lvgl::lvgl LovyanGFX Portduino Protobufs) 75 | target_include_directories(tests PRIVATE 76 | ${CMAKE_CURRENT_SOURCE_DIR} 77 | ${CMAKE_CURRENT_SOURCE_DIR}/src 78 | ${CMAKE_CURRENT_SOURCE_DIR}/locale 79 | ${CMAKE_CURRENT_SOURCE_DIR}/portduino 80 | ${CMAKE_CURRENT_SOURCE_DIR}/generated/${GENERATED_VIEW} 81 | ) 82 | set_target_properties(tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 83 | add_test(NAME tests COMMAND tests) 84 | endif() -------------------------------------------------------------------------------- /cmake/Doctest.cmake: -------------------------------------------------------------------------------- 1 | if(ENABLE_DOCTESTS) 2 | message(STATUS "Fetching doctest ...") 3 | add_definitions(-DENABLE_DOCTEST_IN_LIBRARY) 4 | FetchContent_Declare( 5 | DocTest 6 | GIT_REPOSITORY "https://github.com/onqtam/doctest" 7 | GIT_TAG v2.4.11 8 | ) 9 | 10 | FetchContent_MakeAvailable(DocTest) 11 | include_directories(${DOCTEST_INCLUDE_DIR}) 12 | message("-- adding include_directories(${DOCTEST_INCLUDE_DIR})") 13 | endif() 14 | -------------------------------------------------------------------------------- /cmake/LVGL.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "Fetching LVGL ...") 2 | add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE) 3 | add_compile_definitions(LV_CONF_INCLUDE_SIMPLE) 4 | add_compile_definitions(LV_COMP_CONF_INCLUDE_SIMPLE) 5 | add_compile_definitions(LV_BUILD_TEST=0) 6 | add_compile_definitions(LV_USE_LIBINPUT=1) 7 | add_compile_definitions(LV_LIBINPUT_XKB=1) 8 | FetchContent_Declare(lvgl 9 | GIT_REPOSITORY https://github.com/lvgl/lvgl.git 10 | GIT_TAG "9c043167685fc08fbcd30ddf2c285ea1089be82d" 11 | ) 12 | FetchContent_MakeAvailable(lvgl) 13 | target_include_directories(lvgl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 14 | include_directories(${lvgl_SOURCE_DIR}/src) 15 | -------------------------------------------------------------------------------- /cmake/LovyanGFX.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "Fetching LovyanGFX ...") 2 | FetchContent_Declare( 3 | LovyanGFX 4 | GIT_REPOSITORY "https://github.com/lovyan03/LovyanGFX" 5 | GIT_TAG 1.2.0 6 | ) 7 | FetchContent_MakeAvailable(LovyanGFX) 8 | include_directories(${lovyangfx_SOURCE_DIR}/src) 9 | 10 | # Add the LovyanGFX library 11 | file(GLOB_RECURSE LOVYANGFX_SOURCES ${lovyangfx_SOURCE_DIR}/src/*.cpp) 12 | add_library(LovyanGFX STATIC ${LOVYANGFX_SOURCES}) 13 | target_include_directories(LovyanGFX PUBLIC ${lovyangfx_SOURCE_DIR}/src) -------------------------------------------------------------------------------- /cmake/Portduino.cmake: -------------------------------------------------------------------------------- 1 | # Include Portduino native framework 2 | message(STATUS "Fetching Portduino ...") 3 | FetchContent_Declare( 4 | Portduino 5 | GIT_REPOSITORY "https://github.com/mverch67/framework-portduino" 6 | GIT_TAG "ce0ed7e9c62f3082b30ee9a5c369f30c1e55194a" 7 | ) 8 | FetchContent_MakeAvailable(Portduino) 9 | include_directories(${portduino_SOURCE_DIR}/cores/portduino) 10 | include_directories(${portduino_SOURCE_DIR}/cores/portduino/FS) 11 | include_directories(${portduino_SOURCE_DIR}/cores/arduino) 12 | include_directories(${portduino_SOURCE_DIR}/libraries/SPI/src) 13 | include_directories(${portduino_SOURCE_DIR}/libraries/Wire/src) 14 | include_directories(${portduino_SOURCE_DIR}/libraries/WiFi/src) 15 | include_directories(${portduino_SOURCE_DIR}/ArduinoCore-API/api) 16 | 17 | # Specify source files for Portduino 18 | file(GLOB_RECURSE PORTDUINO_SOURCES 19 | ${portduino_SOURCE_DIR}/cores/portduino/*.c* 20 | ${portduino_SOURCE_DIR}/cores/portduino/linux/*.c* 21 | ${portduino_SOURCE_DIR}/cores/portduino/linux/gpio/*.c* 22 | ${portduino_SOURCE_DIR}/cores/portduino/FS/*.c* 23 | ${portduino_SOURCE_DIR}/cores/portduino/simulated/*.c* 24 | ${portduino_SOURCE_DIR}/cores/arduino/*.c* 25 | ${portduino_SOURCE_DIR}/ArduinoCore-API/api/*.c* 26 | ) 27 | -------------------------------------------------------------------------------- /cmake/esp32_toolchain.cmake: -------------------------------------------------------------------------------- 1 | # Set the name of the C and C++ compilers 2 | set(CMAKE_SYSTEM_NAME Generic) 3 | set(CMAKE_C_COMPILER xtensa-esp32-elf-gcc) 4 | set(CMAKE_CXX_COMPILER xtensa-esp32-elf-g++) 5 | 6 | # Set the path to the compiler tools 7 | set(TOOLCHAIN_PATH "$ENV{HOME}/.platformio/packages/toolchain-xtensa-esp32/bin") 8 | 9 | # Set the compiler flags 10 | set(CMAKE_C_FLAGS "-mlongcalls -mtext-section-literals") 11 | set(CMAKE_CXX_FLAGS "-mlongcalls -mtext-section-literals") 12 | 13 | # Set the linker flags 14 | set(CMAKE_EXE_LINKER_FLAGS "-nostdlib") 15 | 16 | # Set the path to the ESP32 SDK 17 | set(ESP32_SDK_PATH "$ENV{HOME}/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32") 18 | 19 | # Include directories for the ESP32 SDK 20 | include_directories(${ESP32_SDK_PATH}/include) 21 | include_directories(${ESP32_SDK_PATH}/include/esp_common) 22 | include_directories(${ESP32_SDK_PATH}/include/esp_common/include) 23 | include_directories(${ESP32_SDK_PATH}/include/esp32) 24 | include_directories(${ESP32_SDK_PATH}/include/heap) 25 | include_directories(${ESP32_SDK_PATH}/include/log) 26 | include_directories(${ESP32_SDK_PATH}/include/newlib) 27 | include_directories(${ESP32_SDK_PATH}/include/soc) 28 | include_directories(${ESP32_SDK_PATH}/include/xtensa) 29 | include_directories(${ESP32_SDK_PATH}/include/xtensa/include) 30 | include_directories(${ESP32_SDK_PATH}/include/freertos/include/esp_additions/freertos) 31 | include_directories(${ESP32_SDK_PATH}/include/freertos/port/xtensa/include) 32 | include_directories(${ESP32_SDK_PATH}/qio_qspi/include) 33 | -------------------------------------------------------------------------------- /cmake/nanopb.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "Fetching nanopb ...") 2 | #set(nanopb_BUILD_GENERATOR "OFF") 3 | #set(nanopb_BUILD_RUNTIME "OFF") 4 | FetchContent_Declare( 5 | nanopb 6 | GIT_REPOSITORY https://github.com/nanopb/nanopb.git 7 | GIT_TAG 0.4.9.1 8 | ) 9 | FetchContent_MakeAvailable(nanopb) 10 | 11 | # Set paths for protobuf files and generated files 12 | set(PROTO_FILES_DIR ${protobuf_SOURCE_DIR}/meshtastic) 13 | set(GENERATED_FILES_DIR ${CMAKE_CURRENT_BINARY_DIR}/mesh/generated) 14 | 15 | message("-- CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}") 16 | message("-- PROTO_FILES_DIR=${PROTO_FILES_DIR}") 17 | message("-- GENERATED_FILES_DIR=${GENERATED_FILES_DIR}") 18 | 19 | # Create the output directory if it does not exist 20 | file(MAKE_DIRECTORY ${GENERATED_FILES_DIR}/meshtastic) 21 | 22 | # Create a custom command to generate C++ files from .proto files 23 | file(GLOB PROTO_FILES ${PROTO_FILES_DIR}/*.proto) 24 | set(GENERATED_FILES) 25 | foreach(PROTO_FILE ${PROTO_FILES}) 26 | get_filename_component(PROTO_FILE_NAME ${PROTO_FILE} NAME_WE) 27 | add_custom_command( 28 | OUTPUT ${GENERATED_FILES_DIR}/meshtastic/${PROTO_FILE_NAME}.pb.cpp ${GENERATED_FILES_DIR}/${PROTO_FILE_NAME}.pb.h 29 | COMMAND ${nanopb_SOURCE_DIR}/generator/protoc --experimental_allow_proto3_optional --nanopb_out=-S.cpp:${GENERATED_FILES_DIR} -I=${protobuf_SOURCE_DIR} -I=${PROTO_FILES_DIR} 30 | ${PROTO_FILE} 31 | DEPENDS ${PROTO_FILE} 32 | WORKING_DIRECTORY ${protobuf_SOURCE_DIR} 33 | COMMENT "Generating C++ files from ${PROTO_FILE}" 34 | ) 35 | list(APPEND GENERATED_SOURCES ${GENERATED_FILES_DIR}/meshtastic/${PROTO_FILE_NAME}.pb.cpp) 36 | list(APPEND GENERATED_HEADERS ${GENERATED_FILES_DIR}/meshtastic/${PROTO_FILE_NAME}.pb.h) 37 | endforeach() 38 | 39 | # Add the generated files to the project 40 | add_library(Protobufs ${GENERATED_SOURCES}) 41 | target_include_directories(Protobufs PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${GENERATED_FILES_DIR} ${nanopb_SOURCE_DIR}) 42 | -------------------------------------------------------------------------------- /cmake/protobuf.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "Fetching protobufs ...") 2 | FetchContent_Declare( 3 | Protobuf 4 | GIT_REPOSITORY "https://github.com/meshtastic/protobufs" 5 | GIT_TAG v2.6.2 6 | ) 7 | FetchContent_MakeAvailable(Protobuf) 8 | -------------------------------------------------------------------------------- /docs/CYD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/CYD.png -------------------------------------------------------------------------------- /docs/Pi400-TFT.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/Pi400-TFT.jpg -------------------------------------------------------------------------------- /docs/T-Deck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/T-Deck.jpg -------------------------------------------------------------------------------- /docs/X11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/X11.png -------------------------------------------------------------------------------- /docs/class-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/class-diagram.png -------------------------------------------------------------------------------- /docs/tile_pyramid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/docs/tile_pyramid.png -------------------------------------------------------------------------------- /drivers/Touch_CST820.cpp: -------------------------------------------------------------------------------- 1 | //TODO: preliminary version; needs to be adapted to lovyanGFX layer and maybe merged as PR 2 | 3 | #include "Touch_CST820.h" 4 | #include "Arduino.h" 5 | #include "Wire.h" 6 | 7 | #define I2C_ADDR_CST820 0x15 8 | 9 | namespace lgfx 10 | { 11 | inline namespace v1 12 | { 13 | 14 | bool Touch_CST820::init(void) 15 | { 16 | // Initialize I2C 17 | if (_sda != -1 && _scl != -1) { 18 | Wire.begin(_sda, _scl); 19 | } 20 | else { 21 | Wire.begin(); 22 | } 23 | 24 | // Int Pin Configuration 25 | if (_int != -1) { 26 | pinMode(_int, OUTPUT); 27 | digitalWrite(_int, HIGH); 28 | delay(1); 29 | digitalWrite(_int, LOW); 30 | delay(1); 31 | } 32 | 33 | // Reset Pin Configuration 34 | if (_rst != -1) { 35 | pinMode(_rst, OUTPUT); 36 | digitalWrite(_rst, LOW); 37 | delay(10); 38 | digitalWrite(_rst, HIGH); 39 | delay(300); 40 | } 41 | 42 | // Initialize Touch 43 | i2c_write(0xFE, 0XFF); 44 | return true; 45 | } 46 | 47 | uint_fast8_t Touch_CST820::getTouchRaw(touch_point_t* tp, uint_fast8_t count) 48 | { 49 | uint8_t FingerIndex = i2c_read(0x02); 50 | uint8_t gesture = i2c_read(0x01); 51 | //if (!(gesture == SWIPE_UP || gesture == SWIPE_DOWN)) { 52 | // gesture = NONE; 53 | //} 54 | 55 | uint8_t readdata[4]; 56 | i2c_read_continuous(0x03, readdata, 4); 57 | uint16_t x = ((readdata[0] & 0x0f) << 8) | readdata[1]; 58 | uint16_t y = ((readdata[2] & 0x0f) << 8) | readdata[3]; 59 | 60 | tp[0].id = 0; 61 | tp[0].size = 1; 62 | tp[0].x = x; // readdata[2] | (readdata[1] & 0x0F) << 8; 63 | tp[0].y = y; //readdata[4] | (readdata[3] & 0x0F) << 8; 64 | 65 | return FingerIndex; 66 | } 67 | 68 | uint8_t Touch_CST820::i2c_read(uint8_t addr) 69 | { 70 | uint8_t rdData = 0; 71 | uint8_t rdDataCount; 72 | uint8_t loopCount = 0; 73 | do { 74 | Wire.beginTransmission(I2C_ADDR_CST820); 75 | Wire.write(addr); 76 | Wire.endTransmission(false); // Restart 77 | rdDataCount = Wire.requestFrom(I2C_ADDR_CST820, 1); 78 | } while (rdDataCount == 0 && loopCount++ < 10); 79 | 80 | while (Wire.available()) { 81 | rdData = Wire.read(); 82 | } 83 | return rdData; 84 | } 85 | 86 | uint8_t Touch_CST820::i2c_read_continuous(uint8_t addr, uint8_t *data, uint32_t length) 87 | { 88 | Wire.beginTransmission(I2C_ADDR_CST820); 89 | Wire.write(addr); 90 | if (Wire.endTransmission(true)) return -1; 91 | Wire.requestFrom(I2C_ADDR_CST820, length); 92 | 93 | for (int i = 0; i < length; i++) { 94 | *data++ = Wire.read(); 95 | } 96 | return 0; 97 | } 98 | 99 | void Touch_CST820::i2c_write(uint8_t addr, uint8_t data) 100 | { 101 | Wire.beginTransmission(I2C_ADDR_CST820); 102 | Wire.write(addr); 103 | Wire.write(data); 104 | Wire.endTransmission(); 105 | } 106 | 107 | uint8_t Touch_CST820::i2c_write_continuous(uint8_t addr, const uint8_t *data, uint32_t length) 108 | { 109 | Wire.beginTransmission(I2C_ADDR_CST820); 110 | Wire.write(addr); 111 | for (int i = 0; i < length; i++) { 112 | Wire.write(*data++); 113 | } 114 | if (Wire.endTransmission(true)) return -1; 115 | return 0; 116 | } 117 | 118 | 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /drivers/Touch_CST820.h: -------------------------------------------------------------------------------- 1 | #ifndef _CST820_H 2 | #define _CST820_H 3 | 4 | #include "lgfx/v1/Touch.hpp" 5 | 6 | 7 | namespace lgfx 8 | { 9 | inline namespace v1 10 | { 11 | 12 | /** 13 | * @brief CST820 I2C CTP controller driver 14 | * 15 | */ 16 | class Touch_CST820 : public ITouch 17 | { 18 | enum CS820_GESTURE { 19 | NONE = 0x00, 20 | SWIPE_UP = 0x01, 21 | SWIPE_DOWN = 0x02, 22 | SWIPE_LEFT = 0x03, 23 | SWIPE_RIGHT = 0x04, 24 | SINGLE_CLICK = 0x05, 25 | DOUBLE_CLICK = 0x0B, 26 | LONG_PRESS = 0x0C 27 | }; 28 | 29 | public: 30 | Touch_CST820(int8_t sda_pin = -1, int8_t scl_pin = -1, int8_t rst_pin = -1, int8_t int_pin = -1) { 31 | _sda = sda_pin; 32 | _scl = scl_pin; 33 | _rst = rst_pin; 34 | _int = int_pin; 35 | _cfg.i2c_addr = 0x15; 36 | _cfg.x_min = 0; 37 | _cfg.x_max = 320; 38 | _cfg.y_min = 0; 39 | _cfg.y_max = 320; 40 | }; 41 | 42 | bool init(void) override; 43 | 44 | void wakeup(void) override {}; 45 | void sleep(void) override {}; 46 | 47 | uint_fast8_t getTouchRaw(touch_point_t* tp, uint_fast8_t count) override; 48 | 49 | private: 50 | int8_t _sda, _scl, _rst, _int; 51 | 52 | uint8_t i2c_read(uint8_t addr); 53 | uint8_t i2c_read_continuous(uint8_t addr, uint8_t *data, uint32_t length); 54 | void i2c_write(uint8_t addr, uint8_t data); 55 | uint8_t i2c_write_continuous(uint8_t addr, const uint8_t *data, uint32_t length); 56 | }; 57 | 58 | } 59 | } 60 | #endif -------------------------------------------------------------------------------- /extra_script.py: -------------------------------------------------------------------------------- 1 | # See https://docs.platformio.org/en/latest/manifests/library-json/fields/build/extrascript.html 2 | Import("env") 3 | from os.path import join, realpath 4 | # Base srcFilter. Cannot be set in library.json. 5 | src_filter = [ 6 | "+", 7 | "+", 8 | "+" 9 | ] 10 | 11 | for item in env.get("CPPDEFINES", []): 12 | # Add generated view directory to include path dependending on VIEW_* macro 13 | if isinstance(item,str) and item.startswith("VIEW_"): 14 | view = f"ui_{item[5:]}".lower() # Ex value: "ui_320x240" 15 | env.Append(CPPPATH=[realpath(join("generated", view))]) 16 | src_filter.append(f"+") 17 | # Add portduino directory to include path dependending on ARCH_PORTDUINO macro 18 | elif item == "ARCH_PORTDUINO": 19 | env.Append(CPPPATH=[realpath("portduino")]) 20 | src_filter.append("+") 21 | 22 | # Only `Replace` is supported for SRC_FILTER, not `Append` or `Prepend` 23 | env.Replace(SRC_FILTER=src_filter) 24 | 25 | # Dump construction environment (for debug purposes) 26 | # print("meshtastic-device-ui Library ENV:") 27 | # print(env.Dump()) 28 | -------------------------------------------------------------------------------- /generated/ui_128x32/README.md: -------------------------------------------------------------------------------- 1 | // This directory contains the exported ui files. Use eez-studio to design the UI and generate the C code. -------------------------------------------------------------------------------- /generated/ui_128x64/README.md: -------------------------------------------------------------------------------- 1 | // This directory contains the exported ui files. Use eez-studio to design the UI and generate the C code. -------------------------------------------------------------------------------- /generated/ui_160x80/README.md: -------------------------------------------------------------------------------- 1 | // This directory contains the exported ui files. Use eez-studio to design the UI and generate the C code. -------------------------------------------------------------------------------- /generated/ui_240x240/README.md: -------------------------------------------------------------------------------- 1 | // This directory contains the exported ui files. Use eez-studio to design the UI and generate the C code. -------------------------------------------------------------------------------- /generated/ui_320x240/README.md: -------------------------------------------------------------------------------- 1 | // This directory contains the exported ui files. Use eez-studio to design the UI and generate the C code. 2 | -------------------------------------------------------------------------------- /generated/ui_320x240/actions.h: -------------------------------------------------------------------------------- 1 | #ifndef EEZ_LVGL_UI_ACTIONS_H 2 | #define EEZ_LVGL_UI_ACTIONS_H 3 | 4 | #include "lvgl.h" 5 | 6 | extern void action_on_boot_screen_displayed(lv_event_t * e); 7 | 8 | 9 | #endif /*EEZ_LVGL_UI_ACTIONS_H*/ -------------------------------------------------------------------------------- /generated/ui_320x240/fonts.h: -------------------------------------------------------------------------------- 1 | #ifndef EEZ_LVGL_UI_FONTS_H 2 | #define EEZ_LVGL_UI_FONTS_H 3 | 4 | #include "lvgl.h" 5 | 6 | extern const lv_font_t ui_font_montserrat_12; 7 | extern const lv_font_t ui_font_montserrat_14; 8 | extern const lv_font_t ui_font_montserrat_16; 9 | extern const lv_font_t ui_font_montserrat_20; 10 | 11 | 12 | #endif /*EEZ_LVGL_UI_FONTS_H*/ -------------------------------------------------------------------------------- /generated/ui_320x240/ui.c: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | #include "images.h" 3 | #include 4 | 5 | static int16_t currentScreen = -1; 6 | 7 | static lv_obj_t *getLvglObjectFromIndex(int32_t index) { 8 | if (index == -1) { 9 | return 0; 10 | } 11 | return ((lv_obj_t **)&objects)[index]; 12 | } 13 | 14 | static const void *getLvglImageByName(const char *name) { 15 | for (size_t imageIndex = 0; imageIndex < sizeof(images) / sizeof(ext_img_desc_t); imageIndex++) { 16 | if (strcmp(images[imageIndex].name, name) == 0) { 17 | return images[imageIndex].img_dsc; 18 | } 19 | } 20 | return 0; 21 | } 22 | 23 | void loadScreen(enum ScreensEnum screenId) { 24 | currentScreen = screenId - 1; 25 | lv_obj_t *screen = getLvglObjectFromIndex(currentScreen); 26 | lv_screen_load_anim(screen, LV_SCR_LOAD_ANIM_NONE, 200, 0, false); 27 | } 28 | 29 | void ui_init_boot() { 30 | lv_disp_t *dispp = lv_disp_get_default(); 31 | lv_theme_t *theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), false, LV_FONT_DEFAULT); 32 | lv_disp_set_theme(dispp, theme); 33 | 34 | create_screen_boot_screen(); 35 | create_screen_blank_screen(); 36 | loadScreen(SCREEN_ID_BOOT_SCREEN); 37 | } 38 | 39 | void ui_init() { 40 | //create_screens(); 41 | create_screen_main_screen(); 42 | create_screen_lock_screen(); 43 | create_screen_calibration_screen(); 44 | create_tabview_settings(); 45 | } 46 | 47 | void ui_tick() { 48 | tick_screen(currentScreen); 49 | } 50 | -------------------------------------------------------------------------------- /generated/ui_320x240/ui.h: -------------------------------------------------------------------------------- 1 | #ifndef EEZ_LVGL_UI_GUI_H 2 | #define EEZ_LVGL_UI_GUI_H 3 | 4 | #include "lvgl.h" 5 | #include "fonts.h" 6 | #include "screens.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | void ui_init_boot(); 13 | void ui_init(); 14 | void ui_tick(); 15 | 16 | void loadScreen(enum ScreensEnum screenId); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif // EEZ_LVGL_UI_GUI_H -------------------------------------------------------------------------------- /generated/ui_320x240/ui_image_knob_logo_image.c: -------------------------------------------------------------------------------- 1 | #ifdef __has_include 2 | #if __has_include("lvgl.h") 3 | #ifndef LV_LVGL_H_INCLUDE_SIMPLE 4 | #define LV_LVGL_H_INCLUDE_SIMPLE 5 | #endif 6 | #endif 7 | #endif 8 | 9 | #if defined(LV_LVGL_H_INCLUDE_SIMPLE) 10 | #include "lvgl.h" 11 | #elif defined(LV_BUILD_TEST) 12 | #include "../lvgl.h" 13 | #else 14 | #include "lvgl/lvgl.h" 15 | #endif 16 | 17 | 18 | #ifndef LV_ATTRIBUTE_MEM_ALIGN 19 | #define LV_ATTRIBUTE_MEM_ALIGN 20 | #endif 21 | 22 | #ifndef LV_ATTRIBUTE_IMG_KNOB_LOGO_IMAGE 23 | #define LV_ATTRIBUTE_IMG_KNOB_LOGO_IMAGE 24 | #endif 25 | 26 | static const 27 | LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_KNOB_LOGO_IMAGE 28 | uint8_t img_knob_logo_image_map[] = { 29 | 30 | 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x03,0xff,0xff,0xff,0x5b,0xff,0xff,0xff,0x2a,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x33,0xff,0xff,0xff,0x55,0xff,0xff,0xff,0x01,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, 31 | 0x34,0x34,0x34,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x42,0xff,0xff,0xff,0xe3,0xff,0xff,0xff,0x55,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x11,0xff,0xff,0xff,0xba,0xff,0xff,0xff,0xea,0xff,0xff,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00, 32 | 0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x15,0xff,0xff,0xff,0xc4,0xff,0xff,0xff,0xa9,0xff,0xff,0xff,0x0a,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x7c,0xff,0xff,0xff,0xd9,0xff,0xff,0xff,0xbf,0xff,0xff,0xff,0xbc,0xff,0xff,0xff,0x12,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00, 33 | 0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x84,0xff,0xff,0xff,0xdc,0xff,0xff,0xff,0x2b,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x3c,0xff,0xff,0xff,0xe2,0xff,0xff,0xff,0x60,0xff,0xff,0xff,0x26,0xff,0xff,0xff,0xd8,0xff,0xff,0xff,0x7e,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00, 34 | 0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x43,0xff,0xff,0xff,0xe6,0xff,0xff,0xff,0x64,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x12,0xff,0xff,0xff,0xbe,0xff,0xff,0xff,0xa6,0xff,0xff,0xff,0x08,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x61,0xff,0xff,0xff,0xe2,0xff,0xff,0xff,0x3e,0xff,0xff,0xff,0x00, 35 | 0xff,0xff,0xff,0x1a,0xff,0xff,0xff,0xc3,0xff,0xff,0xff,0xa8,0xff,0xff,0xff,0x08,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x7e,0xff,0xff,0xff,0xd8,0xff,0xff,0xff,0x28,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x07,0xff,0xff,0xff,0xa3,0xff,0xff,0xff,0xbe,0xff,0xff,0xff,0x18, 36 | 0xff,0xff,0xff,0x74,0xff,0xff,0xff,0xd3,0xff,0xff,0xff,0x2a,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x25,0xff,0xff,0xff,0xd3,0xff,0xff,0xff,0x61,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x26,0xff,0xff,0xff,0xcf,0xff,0xff,0xff,0x70, 37 | 0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0x43,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x38,0xff,0xff,0xff,0x23, 38 | 39 | }; 40 | 41 | const lv_image_dsc_t img_knob_logo_image = { 42 | .header.magic = LV_IMAGE_HEADER_MAGIC, 43 | .header.cf = LV_COLOR_FORMAT_ARGB8888, 44 | .header.flags = 0, 45 | .header.w = 14, 46 | .header.h = 8, 47 | .header.stride = 56, 48 | .data_size = sizeof(img_knob_logo_image_map), 49 | .data = img_knob_logo_image_map, 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /generated/ui_320x240/ui_image_location_pin_14_image.c: -------------------------------------------------------------------------------- 1 | #ifdef __has_include 2 | #if __has_include("lvgl.h") 3 | #ifndef LV_LVGL_H_INCLUDE_SIMPLE 4 | #define LV_LVGL_H_INCLUDE_SIMPLE 5 | #endif 6 | #endif 7 | #endif 8 | 9 | #if defined(LV_LVGL_H_INCLUDE_SIMPLE) 10 | #include "lvgl.h" 11 | #elif defined(LV_BUILD_TEST) 12 | #include "../lvgl.h" 13 | #else 14 | #include "lvgl/lvgl.h" 15 | #endif 16 | 17 | 18 | #ifndef LV_ATTRIBUTE_MEM_ALIGN 19 | #define LV_ATTRIBUTE_MEM_ALIGN 20 | #endif 21 | 22 | #ifndef LV_ATTRIBUTE_IMG_DUST 23 | #define LV_ATTRIBUTE_IMG_DUST 24 | #endif 25 | 26 | static const 27 | LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DUST 28 | uint8_t img_location_pin_14_image_map[] = { 29 | 30 | 0x00,0x00,0x00,0x00,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x3c,0xe7,0xfb,0xde,0xfb,0xde,0x3c,0xe7,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x00,0x00,0x00,0x00, 31 | 0x00,0x00,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x00,0x00, 32 | 0x00,0x00,0xdb,0xde,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0xdb,0xde,0x00,0x00, 33 | 0xdb,0xde,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0xdb,0xde, 34 | 0xdb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xdb,0xde, 35 | 0xfb,0xde,0xff,0xff,0x1c,0xe7,0x1c,0xe7,0x9e,0xf7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x9e,0xf7,0x1c,0xe7,0x1c,0xe7,0xff,0xff,0xfb,0xde, 36 | 0xfb,0xde,0x7d,0xef,0x1c,0xe7,0x1c,0xe7,0x3c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x3c,0xe7,0x1c,0xe7,0x1c,0xe7,0x7d,0xef,0xfb,0xde, 37 | 0x9a,0xd6,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x9a,0xd6, 38 | 0xdb,0xde,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0xdb,0xde, 39 | 0x00,0x00,0x18,0xc6,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0xfb,0xde,0x1c,0xe7,0x38,0xc6,0x00,0x00, 40 | 0x00,0x00,0xfb,0xde,0xfb,0xde,0xdb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xdb,0xde,0xfb,0xde,0xfb,0xde,0x00,0x00, 41 | 0x00,0x00,0x00,0x00,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x00,0x00,0x00,0x00, 42 | 0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x1c,0xe7,0x1c,0xe7,0xfb,0xde,0xfb,0xde,0xfb,0xde,0x00,0x00,0x00,0x00,0x00,0x00, 43 | 0x00,0x00,0x00,0x00,0x00,0x00,0xf7,0xbd,0xfb,0xde,0xfb,0xde,0xdb,0xde,0xdb,0xde,0xfb,0xde,0xfb,0xde,0x96,0xb5,0x00,0x00,0x00,0x00,0x00,0x00, 44 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x90,0xbe,0xbe,0x90,0x30,0x00,0x00,0x00,0x00, 45 | 0x00,0x00,0x00,0x4e,0xd8,0xaf,0x6a,0x6a,0xaf,0xd8,0x4e,0x00,0x00,0x00,0x00,0x00,0x24,0xd3,0x7d,0x05,0x07,0x07,0x05,0x7d,0xd3,0x24,0x00,0x00, 46 | 0x00,0x00,0x74,0xc2,0x0b,0x3a,0xbd,0xbd,0x3a,0x0b,0xc2,0x74,0x00,0x00,0x00,0x00,0xa2,0x8e,0x00,0xa9,0xad,0xad,0xa9,0x00,0x8d,0xa2,0x00,0x00, 47 | 0x00,0x00,0x9e,0x92,0x00,0x8f,0xcf,0xcf,0x8f,0x00,0x92,0x9f,0x00,0x00,0x00,0x00,0x6a,0xc9,0x12,0x17,0x7a,0x7a,0x17,0x11,0xc9,0x6a,0x00,0x00, 48 | 0x00,0x00,0x1d,0xcf,0x76,0x00,0x00,0x00,0x00,0x76,0xcf,0x1d,0x00,0x00,0x00,0x00,0x00,0x58,0xde,0x4c,0x00,0x00,0x4c,0xde,0x59,0x00,0x00,0x00, 49 | 0x00,0x00,0x00,0x02,0x7d,0xd9,0x3f,0x3f,0xd9,0x7d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x8a,0xdd,0xdd,0x8a,0x07,0x00,0x00,0x00,0x00, 50 | 0x00,0x00,0x00,0x00,0x00,0x09,0x84,0x84,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00, 51 | 52 | }; 53 | 54 | const lv_img_dsc_t img_location_pin_14_image = { 55 | .header.magic = LV_IMAGE_HEADER_MAGIC, 56 | .header.cf = LV_COLOR_FORMAT_RGB565A8, 57 | .header.flags = 0, 58 | .header.w = 14, 59 | .header.h = 14, 60 | .header.stride = 28, 61 | .data_size = 588, 62 | .data = img_location_pin_14_image_map, 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /generated/ui_320x240/vars.h: -------------------------------------------------------------------------------- 1 | #ifndef EEZ_LVGL_UI_VARS_H 2 | #define EEZ_LVGL_UI_VARS_H 3 | 4 | #include 5 | 6 | 7 | 8 | #endif /*EEZ_LVGL_UI_VARS_H*/ -------------------------------------------------------------------------------- /include/comms/BluetoothClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/SerialClient.h" 4 | #include 5 | 6 | class BluetoothClient : public SerialClient 7 | { 8 | public: 9 | BluetoothClient(void); 10 | void init(void) override; 11 | bool connect(void) override; 12 | bool disconnect(void) override; 13 | bool isConnected(void) override; 14 | bool send(meshtastic_ToRadio &&to) override; 15 | meshtastic_FromRadio receive(void) override; 16 | virtual ~BluetoothClient(); 17 | 18 | protected: 19 | // low-level send method to write the buffer to serial 20 | bool send(const uint8_t *buf, size_t len); 21 | 22 | // low-level receive method, periodically being called via thread 23 | size_t receive(uint8_t *buf, size_t space_left); 24 | 25 | time_t lastReceived; 26 | }; -------------------------------------------------------------------------------- /include/comms/EthClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Client.h" 4 | #include "comms/SerialClient.h" 5 | #include "util/SharedQueue.h" 6 | #include 7 | 8 | #ifndef SERVER_PORT 9 | #define SERVER_PORT 4403 10 | #endif 11 | 12 | class EthClient : public SerialClient 13 | { 14 | public: 15 | EthClient(const char *serverName = "localhost", uint16_t port = SERVER_PORT); 16 | void init(void) override; 17 | bool connect(void) override; 18 | bool disconnect(void) override; 19 | bool isConnected(void) override; 20 | // bool send(meshtastic_ToRadio &&to) override; 21 | meshtastic_FromRadio receive(void) override; 22 | virtual ~EthClient(); 23 | 24 | protected: 25 | // low-level send method to write the encoded buffer to ethernet 26 | bool send(const uint8_t *buf, size_t len) override; 27 | 28 | // low-level receive method, periodically being called via thread 29 | size_t receive(uint8_t *buf, size_t space_left) override; 30 | 31 | Client *client; 32 | uint8_t mac[6]; 33 | IPAddress localIP; 34 | IPAddress serverIP; 35 | const char *server; 36 | uint16_t serverPort; 37 | }; -------------------------------------------------------------------------------- /include/comms/IClientBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mesh-pb-constants.h" 4 | #include "stdint.h" 5 | #include 6 | 7 | /** 8 | * @brief Communication interface to be implemented by the user of the device-ui 9 | * library. The interface implementation has to be passed when initializing the 10 | * DeviceScreen. 11 | * @tparam T 12 | */ 13 | 14 | class IClientBase 15 | { 16 | public: 17 | enum ConnectionStatus { eDisconnected = 0, eConnected, eConnecting, eDisconnecting, eError }; 18 | 19 | using NotifyCallback = std::function; 20 | 21 | virtual void init(void) = 0; 22 | virtual bool connect(void) = 0; 23 | virtual bool disconnect(void) = 0; 24 | virtual bool isConnected(void) = 0; 25 | virtual bool isStandalone(void) = 0; 26 | virtual bool sleep(int16_t pin) { return false; } 27 | 28 | virtual bool send(meshtastic_ToRadio &&to) = 0; 29 | virtual meshtastic_FromRadio receive(void) = 0; 30 | virtual ~IClientBase(){}; 31 | 32 | virtual void task_handler(void){}; 33 | virtual void setNotifyCallback(NotifyCallback notifyConnectionStatus) = 0; 34 | 35 | protected: 36 | IClientBase() = default; 37 | }; 38 | -------------------------------------------------------------------------------- /include/comms/MeshEnvelope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mesh-pb-constants.h" 4 | #include 5 | #include 6 | 7 | const size_t PB_BUFSIZE = 512; 8 | const int MT_HEADER_SIZE = 4; 9 | 10 | /** 11 | * @brief This class encodes/decodes protobuf streams with magic headers 12 | * 13 | */ 14 | class MeshEnvelope 15 | { 16 | public: 17 | // ctor (1) creates empty vector to add bytes later for encoding 18 | MeshEnvelope(void); 19 | // ctor (2) creates envelope with validated bytestream for decoding 20 | MeshEnvelope(const uint8_t *pb_buf, size_t length); 21 | 22 | // encode envelope created in (1) with data in toRadio 23 | std::vector &encode(const meshtastic_ToRadio &toRadio); 24 | // decode buffer given in (2) 25 | meshtastic_FromRadio decode(void); 26 | 27 | // check for valid packet in byte stream, strip all bytes in front of packet 28 | static bool validate(uint8_t *pb_buf, size_t &pb_size, size_t &payload_len); 29 | // invalidate first packet that has been handled already, prepare for next 30 | static void invalidate(uint8_t *pb_buf, size_t &pb_size, size_t &payload_len); 31 | 32 | ~MeshEnvelope() {} 33 | 34 | protected: 35 | std::vector envelope; 36 | }; -------------------------------------------------------------------------------- /include/comms/PacketClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/IClientBase.h" 4 | 5 | class SharedQueue; 6 | 7 | /** 8 | * @brief Client implementation to receive packets from and 9 | * send packets to the shared queue 10 | * 11 | */ 12 | class PacketClient : public IClientBase 13 | { 14 | public: 15 | PacketClient(); 16 | void init(void) override; 17 | bool connect(void) override; 18 | bool disconnect(void) override; 19 | bool isConnected(void) override; 20 | bool isStandalone(void) override; 21 | bool send(meshtastic_ToRadio &&to) override; 22 | meshtastic_FromRadio receive(void) override; 23 | 24 | virtual bool hasData() const; 25 | virtual bool available() const; 26 | 27 | void task_handler(void) override{}; 28 | void setNotifyCallback(NotifyCallback notifyConnectionStatus) override{}; 29 | virtual ~PacketClient() = default; 30 | 31 | protected: 32 | virtual int connect(SharedQueue *_queue); 33 | 34 | private: 35 | volatile bool is_connected = false; 36 | SharedQueue *queue; 37 | }; 38 | -------------------------------------------------------------------------------- /include/comms/PacketServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/Packet.h" 4 | #include "util/PacketQueue.h" 5 | 6 | class SharedQueue; 7 | 8 | /** 9 | * Generic server implementation (base class) for bidirectional task communication 10 | * Uses a queue that is shared with the client 11 | */ 12 | class PacketServer 13 | { 14 | public: 15 | PacketServer(); 16 | static PacketServer *init(void); 17 | virtual void begin(SharedQueue *_queue); 18 | virtual bool sendPacket(Packet &&p); 19 | virtual Packet::PacketPtr receivePacket(void); 20 | // template variant with typed return values 21 | // template<> Packet::PacketPtr receivePacket() 22 | // template T receivePacket(); 23 | virtual bool hasData() const; 24 | virtual bool available() const; 25 | 26 | private: 27 | SharedQueue *queue; 28 | }; 29 | -------------------------------------------------------------------------------- /include/comms/SerialClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/IClientBase.h" 4 | #include "comms/MeshEnvelope.h" 5 | #include "util/SharedQueue.h" 6 | 7 | class SerialClient : public IClientBase 8 | { 9 | public: 10 | SerialClient(const char *name = "serial"); 11 | void init(void) override; 12 | bool sleep(int16_t pin); 13 | bool connect(void) override; 14 | bool disconnect(void) override; 15 | bool isConnected(void) override; 16 | bool isStandalone(void) override; 17 | bool send(meshtastic_ToRadio &&to) override; 18 | meshtastic_FromRadio receive(void) override; 19 | 20 | void task_handler(void) override; 21 | void setNotifyCallback(NotifyCallback notifyConnectionStatus) override; 22 | virtual ~SerialClient(); 23 | 24 | protected: 25 | // low-level send method to write the encoded buffer to serial 26 | virtual bool send(const uint8_t *buf, size_t len); 27 | 28 | // low-level receive method, periodically being called via thread 29 | virtual size_t receive(uint8_t *buf, size_t space_left); 30 | 31 | // received a full packet from serial, process it 32 | virtual void handlePacketReceived(void); 33 | 34 | // received a full packet from serial, process it 35 | virtual void handleSendPacket(void); 36 | 37 | // status handling, to be called by derived classes 38 | void setConnectionStatus(ConnectionStatus status, const char *info = nullptr); 39 | 40 | // thread handling stuff and data 41 | static void task_loop(void *); 42 | static SerialClient *instance; 43 | 44 | // local buffered data 45 | size_t pb_size; 46 | uint8_t *buffer; 47 | 48 | // callback for connection status 49 | NotifyCallback notifyConnectionStatus; 50 | // reported status 51 | ConnectionStatus connectionStatus; 52 | // status of client connection (set by derived class) 53 | volatile ConnectionStatus clientStatus; 54 | // status details (set by derived class) 55 | const char *connectionInfo; 56 | 57 | // announce client shutdown 58 | volatile bool shutdown; 59 | // instance thread name 60 | const char *threadName; 61 | 62 | // receiver and sender queue 63 | SharedQueue queue; 64 | }; -------------------------------------------------------------------------------- /include/comms/UARTClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "HardwareSerial.h" 4 | #include "comms/SerialClient.h" 5 | #include 6 | 7 | class UARTClient : public SerialClient 8 | { 9 | public: 10 | UARTClient(void); 11 | void init(void) override; 12 | bool connect(void) override; 13 | bool disconnect(void) override; 14 | bool isConnected(void) override; 15 | meshtastic_FromRadio receive(void) override; 16 | virtual ~UARTClient(); 17 | 18 | protected: 19 | // low-level send method to write the buffer to serial 20 | bool send(const uint8_t *buf, size_t len) override; 21 | 22 | // low-level receive method, periodically being called via thread 23 | size_t receive(uint8_t *buf, size_t space_left) override; 24 | 25 | bool isActive; 26 | HardwareSerial *_serial; 27 | time_t lastReceived; 28 | }; -------------------------------------------------------------------------------- /include/comms/WLANClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/SerialClient.h" 4 | #include 5 | 6 | class WLANClient : public SerialClient 7 | { 8 | public: 9 | WLANClient(void); 10 | void init(void) override; 11 | bool connect(void) override; 12 | bool disconnect(void) override; 13 | bool isConnected(void) override; 14 | bool send(meshtastic_ToRadio &&to) override; 15 | meshtastic_FromRadio receive(void) override; 16 | virtual ~WLANClient(); 17 | 18 | protected: 19 | // low-level send method to write the encoded buffer to WLAN 20 | bool send(const uint8_t *buf, size_t len); 21 | 22 | // low-level receive method, periodically being called via thread 23 | size_t receive(uint8_t *buf, size_t space_left); 24 | 25 | time_t lastReceived; 26 | }; -------------------------------------------------------------------------------- /include/graphics/DeviceGUI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class DisplayDriverConfig; 6 | class DisplayDriver; 7 | class InputDriver; 8 | class IClientBase; 9 | 10 | /** 11 | * @brief DeviceGUI - this class hierarchy implements the view part of MVC 12 | * 13 | */ 14 | class DeviceGUI 15 | { 16 | public: 17 | DeviceGUI(const DisplayDriverConfig *cfg, DisplayDriver *driver); 18 | virtual ~DeviceGUI(); 19 | virtual void init(IClientBase *client); 20 | virtual void task_handler(void); 21 | virtual bool sleep(int16_t pin) { return false; } 22 | virtual void triggerHeartbeat(void) {} 23 | // called to inform view to do screen blanking 24 | virtual void blankScreen(bool enable) {} 25 | // called when display (driver) is doing screen blanking 26 | virtual void screenSaving(bool enabled){}; 27 | // return true if screen view is locked 28 | virtual bool isScreenLocked(void) { return false; }; 29 | 30 | DisplayDriver *getDisplayDriver(void) const { return displaydriver; } 31 | InputDriver *getInputDriver(void) const { return inputdriver; } 32 | 33 | protected: 34 | DisplayDriver *displaydriver; 35 | InputDriver *inputdriver; 36 | }; 37 | -------------------------------------------------------------------------------- /include/graphics/DeviceScreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/IClientBase.h" 4 | #include "graphics/DeviceGUI.h" 5 | #include "graphics/driver/DisplayDriverConfig.h" 6 | 7 | #if defined(ARDUINO_ARCH_ESP32) 8 | #include "esp_sleep.h" 9 | #endif 10 | 11 | /** 12 | * @brief DeviceScreen - sets up the GUI and display drivers 13 | * 14 | */ 15 | class DeviceScreen 16 | { 17 | public: 18 | static DeviceScreen &create(void); 19 | static DeviceScreen &create(const DisplayDriverConfig *cfg); 20 | static DeviceScreen &create(DisplayDriverConfig &&cfg); 21 | 22 | void init(IClientBase *client); 23 | void task_handler(void); 24 | 25 | #if defined(ARDUINO_ARCH_ESP32) 26 | int prepareSleep(void *); 27 | int wakeUp(esp_sleep_wakeup_cause_t cause); 28 | #endif 29 | void sleep(uint32_t sleepTime = 5); 30 | 31 | private: 32 | DeviceScreen(const DisplayDriverConfig *cfg); 33 | DeviceScreen(DisplayDriverConfig &&cfg); 34 | DeviceGUI *gui; 35 | }; -------------------------------------------------------------------------------- /include/graphics/LGFX/LGFX_4848S040.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | #define LGFX_USE_V1 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class LGFX_4848S040 : public lgfx::LGFX_Device 10 | { 11 | lgfx::Panel_ST7701_guition_esp32_4848S040 _panel_instance; 12 | lgfx::Bus_RGB _bus_instance; 13 | lgfx::Light_PWM _light_instance; 14 | lgfx::Touch_GT911 _touch_instance; 15 | 16 | public: 17 | const uint16_t screenWidth = 480; 18 | const uint16_t screenHeight = 480; 19 | 20 | bool hasButton(void) { return true; } 21 | 22 | LGFX_4848S040(void) 23 | { 24 | { 25 | auto cfg = _panel_instance.config(); 26 | cfg.memory_width = 480; 27 | cfg.memory_height = 480; 28 | cfg.panel_width = screenWidth; 29 | cfg.panel_height = screenHeight; 30 | cfg.offset_x = 0; 31 | cfg.offset_y = 0; 32 | cfg.offset_rotation = 0; 33 | _panel_instance.config(cfg); 34 | } 35 | 36 | { 37 | auto cfg = _panel_instance.config_detail(); 38 | cfg.pin_cs = 39; 39 | cfg.pin_sclk = 48; 40 | cfg.pin_mosi = 47; 41 | cfg.use_psram = 1; 42 | _panel_instance.config_detail(cfg); 43 | } 44 | 45 | { 46 | auto cfg = _bus_instance.config(); 47 | cfg.panel = &_panel_instance; 48 | 49 | cfg.pin_d0 = GPIO_NUM_4; // DB1(B) 50 | cfg.pin_d1 = GPIO_NUM_5; // DB2(B) 51 | cfg.pin_d2 = GPIO_NUM_6; // DB3(B) 52 | cfg.pin_d3 = GPIO_NUM_7; // DB4(B) 53 | cfg.pin_d4 = GPIO_NUM_15; // DB5(B) 54 | 55 | cfg.pin_d5 = GPIO_NUM_8; // DB6(G) 56 | cfg.pin_d6 = GPIO_NUM_20; // DB7(G) 57 | cfg.pin_d7 = GPIO_NUM_3; // DB8(G) 58 | cfg.pin_d8 = GPIO_NUM_46; // DB9(G) 59 | cfg.pin_d9 = GPIO_NUM_9; // DB10(G) 60 | cfg.pin_d10 = GPIO_NUM_10; // DB11(G) 61 | 62 | cfg.pin_d11 = GPIO_NUM_11; // DB13(R) 63 | cfg.pin_d12 = GPIO_NUM_12; // DB14(R) 64 | cfg.pin_d13 = GPIO_NUM_13; // DB15(R) 65 | cfg.pin_d14 = GPIO_NUM_14; // DB16(R) 66 | cfg.pin_d15 = GPIO_NUM_0; // DB17(R) 67 | 68 | cfg.pin_henable = GPIO_NUM_18; 69 | cfg.pin_vsync = GPIO_NUM_17; 70 | cfg.pin_hsync = GPIO_NUM_16; 71 | cfg.pin_pclk = GPIO_NUM_21; 72 | cfg.freq_write = 12000000; 73 | 74 | cfg.hsync_polarity = 1; 75 | cfg.hsync_front_porch = 10; 76 | cfg.hsync_pulse_width = 8; 77 | cfg.hsync_back_porch = 50; 78 | 79 | cfg.vsync_polarity = 1; 80 | cfg.vsync_front_porch = 10; 81 | cfg.vsync_pulse_width = 8; 82 | cfg.vsync_back_porch = 20; 83 | 84 | cfg.pclk_active_neg = 1; 85 | cfg.de_idle_high = 0; 86 | cfg.pclk_idle_high = 0; 87 | 88 | _bus_instance.config(cfg); 89 | } 90 | _panel_instance.setBus(&_bus_instance); 91 | 92 | { 93 | auto cfg = _light_instance.config(); 94 | cfg.pin_bl = 38; 95 | cfg.freq = 80; // higher value decrease brightness 96 | _light_instance.config(cfg); 97 | } 98 | _panel_instance.light(&_light_instance); 99 | 100 | { 101 | auto cfg = _touch_instance.config(); 102 | cfg.pin_cs = GPIO_NUM_NC; 103 | cfg.x_min = 0; 104 | cfg.x_max = 479; 105 | cfg.y_min = 0; 106 | cfg.y_max = 479; 107 | cfg.pin_int = GPIO_NUM_NC; 108 | cfg.pin_rst = GPIO_NUM_NC; 109 | cfg.bus_shared = false; 110 | cfg.offset_rotation = 0; 111 | 112 | cfg.i2c_port = 0; 113 | cfg.i2c_addr = 0x5D; 114 | cfg.pin_sda = 19; 115 | cfg.pin_scl = 45; 116 | cfg.freq = 400000; 117 | _touch_instance.config(cfg); 118 | _panel_instance.setTouch(&_touch_instance); 119 | } 120 | 121 | setPanel(&_panel_instance); 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /include/graphics/LGFX/LGFX_MAKERFABS480X480.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define LGFX_USE_V1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class LGFX_MAKERFABS480X480 : public lgfx::LGFX_Device 9 | { 10 | lgfx::Panel_ST7701 _panel_instance; 11 | lgfx::Bus_RGB _bus_instance; 12 | lgfx::Light_PWM _light_instance; 13 | lgfx::Touch_GT911 _touch_instance; 14 | 15 | public: 16 | const uint16_t screenWidth = 480; 17 | const uint16_t screenHeight = 480; 18 | 19 | bool hasButton(void) { return false; } 20 | 21 | LGFX_MAKERFABS480X480(void) 22 | { 23 | { 24 | auto cfg = _panel_instance.config(); 25 | cfg.memory_width = 480; 26 | cfg.memory_height = 480; 27 | cfg.panel_width = screenWidth; 28 | cfg.panel_height = screenHeight; 29 | cfg.offset_x = 0; 30 | cfg.offset_y = 0; 31 | cfg.offset_rotation = 0; 32 | _panel_instance.config(cfg); 33 | } 34 | 35 | { 36 | auto cfg = _panel_instance.config_detail(); 37 | cfg.pin_cs = 1; 38 | cfg.pin_sclk = 12; 39 | cfg.pin_mosi = 11; 40 | cfg.use_psram = 1; 41 | _panel_instance.config_detail(cfg); 42 | } 43 | 44 | { 45 | auto cfg = _bus_instance.config(); 46 | cfg.panel = &_panel_instance; 47 | cfg.pin_d0 = GPIO_NUM_6; // B0 48 | cfg.pin_d1 = GPIO_NUM_7; // B1 49 | cfg.pin_d2 = GPIO_NUM_15; // B2 50 | cfg.pin_d3 = GPIO_NUM_16; // B3 51 | cfg.pin_d4 = GPIO_NUM_8; // B4 52 | cfg.pin_d5 = GPIO_NUM_0; // G0 53 | cfg.pin_d6 = GPIO_NUM_9; // G1 54 | cfg.pin_d7 = GPIO_NUM_14; // G2 55 | cfg.pin_d8 = GPIO_NUM_47; // G3 56 | cfg.pin_d9 = GPIO_NUM_48; // G4 57 | cfg.pin_d10 = GPIO_NUM_3; // G5 58 | cfg.pin_d11 = GPIO_NUM_39; // R0 59 | cfg.pin_d12 = GPIO_NUM_40; // R1 60 | cfg.pin_d13 = GPIO_NUM_41; // R2 61 | cfg.pin_d14 = GPIO_NUM_42; // R3 62 | cfg.pin_d15 = GPIO_NUM_2; // R4 63 | 64 | cfg.pin_henable = GPIO_NUM_45; 65 | cfg.pin_vsync = GPIO_NUM_4; 66 | cfg.pin_hsync = GPIO_NUM_5; 67 | cfg.pin_pclk = GPIO_NUM_21; 68 | cfg.freq_write = 14000000; 69 | 70 | cfg.hsync_polarity = 0; 71 | cfg.hsync_front_porch = 10; 72 | cfg.hsync_pulse_width = 8; 73 | cfg.hsync_back_porch = 50; 74 | 75 | cfg.vsync_polarity = 0; 76 | cfg.vsync_front_porch = 10; 77 | cfg.vsync_pulse_width = 8; 78 | cfg.vsync_back_porch = 20; 79 | 80 | cfg.pclk_active_neg = 0; 81 | cfg.pclk_idle_high = 0; 82 | cfg.de_idle_high = 1; 83 | 84 | _bus_instance.config(cfg); 85 | } 86 | _panel_instance.setBus(&_bus_instance); 87 | 88 | { 89 | auto cfg = _light_instance.config(); 90 | cfg.pin_bl = 44; 91 | // cfg.freq = 80; // higher value decrease brightness 92 | _light_instance.config(cfg); 93 | } 94 | _panel_instance.light(&_light_instance); 95 | 96 | { 97 | auto cfg = _touch_instance.config(); 98 | cfg.pin_cs = GPIO_NUM_NC; 99 | cfg.x_min = 0; 100 | cfg.x_max = 479; 101 | cfg.y_min = 0; 102 | cfg.y_max = 479; 103 | cfg.bus_shared = false; 104 | cfg.offset_rotation = 0; 105 | 106 | cfg.i2c_port = 1; 107 | cfg.i2c_addr = 0x5D; 108 | cfg.pin_int = GPIO_NUM_NC; 109 | cfg.pin_sda = GPIO_NUM_17; 110 | cfg.pin_scl = GPIO_NUM_18; 111 | cfg.pin_rst = GPIO_NUM_38; 112 | 113 | cfg.freq = 400000; 114 | _touch_instance.config(cfg); 115 | _panel_instance.setTouch(&_touch_instance); 116 | } 117 | 118 | setPanel(&_panel_instance); 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /include/graphics/LGFX/LGFX_T_HMI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define LGFX_USE_V1 3 | 4 | #include 5 | 6 | class LGFX_T_HMI : public lgfx::LGFX_Device 7 | { 8 | 9 | lgfx::Panel_ST7789 _panel_instance; 10 | lgfx::Bus_Parallel8 _bus_instance; 11 | lgfx::Light_PWM _light_instance; 12 | lgfx::Touch_XPT2046 _touch_instance; 13 | 14 | public: 15 | const uint16_t screenWidth = 320; 16 | const uint16_t screenHeight = 240; 17 | 18 | bool hasButton(void) { return true; } 19 | 20 | LGFX_T_HMI(void) 21 | { 22 | { 23 | auto cfg = _bus_instance.config(); 24 | cfg.freq_write = 20000000; 25 | #if CONFIG_IDF_TARGET_ESP32S3 26 | cfg.freq_read = 16000000; 27 | #endif 28 | cfg.pin_rd = -1; 29 | cfg.pin_wr = 8; 30 | cfg.pin_rs = 7; 31 | cfg.pin_d0 = 48; 32 | cfg.pin_d1 = 47; 33 | cfg.pin_d2 = 39; 34 | cfg.pin_d3 = 40; 35 | cfg.pin_d4 = 41; 36 | cfg.pin_d5 = 42; 37 | cfg.pin_d6 = 45; 38 | cfg.pin_d7 = 46; 39 | _bus_instance.config(cfg); 40 | _panel_instance.setBus(&_bus_instance); 41 | } 42 | 43 | { 44 | auto cfg = _panel_instance.config(); 45 | 46 | cfg.pin_cs = 6; 47 | cfg.pin_rst = -1; 48 | cfg.pin_busy = -1; 49 | 50 | cfg.panel_width = screenHeight; 51 | cfg.panel_height = screenWidth; 52 | cfg.offset_x = 0; 53 | cfg.offset_y = 0; 54 | cfg.offset_rotation = 1; 55 | cfg.dummy_read_pixel = 8; 56 | cfg.dummy_read_bits = 1; 57 | cfg.readable = true; 58 | cfg.invert = false; 59 | cfg.rgb_order = false; 60 | cfg.dlen_16bit = false; 61 | cfg.bus_shared = false; 62 | 63 | _panel_instance.config(cfg); 64 | } 65 | 66 | { 67 | auto cfg = _light_instance.config(); 68 | 69 | cfg.pin_bl = 38; 70 | cfg.invert = false; 71 | cfg.freq = 44100; 72 | cfg.pwm_channel = 7; 73 | 74 | _light_instance.config(cfg); 75 | _panel_instance.setLight(&_light_instance); 76 | } 77 | 78 | { 79 | auto cfg = _touch_instance.config(); 80 | 81 | cfg.x_min = 300; 82 | cfg.x_max = 3900; 83 | cfg.y_min = 400; 84 | cfg.y_max = 3900; 85 | cfg.pin_int = 9; 86 | cfg.pin_cs = 2; 87 | cfg.pin_sclk = 1; 88 | cfg.pin_miso = 4; 89 | cfg.pin_mosi = 3; 90 | cfg.spi_host = SPI3_HOST; 91 | cfg.bus_shared = false; 92 | cfg.offset_rotation = 0; 93 | cfg.freq = 2500000; 94 | 95 | _touch_instance.config(cfg); 96 | _panel_instance.setTouch(&_touch_instance); 97 | } 98 | 99 | setPanel(&_panel_instance); 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /include/graphics/LGFX/LGFX_WT_SC01PLUS.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define LGFX_USE_V1 3 | 4 | #include 5 | 6 | #ifndef SPI_FREQUENCY 7 | #define SPI_FREQUENCY 25000000 8 | #endif 9 | 10 | class LGFX_WT_SC01_PLUS : public lgfx::LGFX_Device 11 | { 12 | 13 | lgfx::Panel_ST7796 _panel_instance; 14 | lgfx::Bus_Parallel8 _bus_instance; 15 | lgfx::Light_PWM _light_instance; 16 | lgfx::Touch_FT5x06 _touch_instance; // FT5206, FT5306, FT5406, FT6206, FT6236, FT6336, FT6436 17 | 18 | public: 19 | #ifdef USE_LANDSCAPE 20 | const uint16_t screenWidth = 480; 21 | const uint16_t screenHeight = 320; 22 | const uint8_t offsetRotation = 1; 23 | #else 24 | const uint16_t screenWidth = 320; 25 | const uint16_t screenHeight = 480; 26 | const uint8_t offsetRotation = 0; 27 | #endif 28 | 29 | bool hasButton(void) { return false; } 30 | 31 | LGFX_WT_SC01_PLUS(void) 32 | { 33 | { 34 | auto cfg = _bus_instance.config(); 35 | cfg.freq_write = SPI_FREQUENCY; 36 | #if CONFIG_IDF_TARGET_ESP32S3 37 | cfg.freq_read = 16000000; 38 | #endif 39 | cfg.pin_wr = 47; 40 | cfg.pin_rd = -1; 41 | cfg.pin_rs = 0; 42 | cfg.pin_d0 = 9; 43 | cfg.pin_d1 = 46; 44 | cfg.pin_d2 = 3; 45 | cfg.pin_d3 = 8; 46 | cfg.pin_d4 = 18; 47 | cfg.pin_d5 = 17; 48 | cfg.pin_d6 = 16; 49 | cfg.pin_d7 = 15; 50 | _bus_instance.config(cfg); 51 | _panel_instance.setBus(&_bus_instance); 52 | } 53 | 54 | { 55 | auto cfg = _panel_instance.config(); 56 | 57 | cfg.pin_cs = -1; 58 | cfg.pin_rst = 4; 59 | cfg.pin_busy = -1; 60 | 61 | #ifdef USE_LANDSCAPE 62 | cfg.panel_width = screenHeight; 63 | cfg.panel_height = screenWidth; 64 | #else 65 | cfg.panel_width = screenWidth; 66 | cfg.panel_height = screenHeight; 67 | #endif 68 | cfg.offset_x = 0; 69 | cfg.offset_y = 0; 70 | cfg.offset_rotation = offsetRotation; 71 | cfg.dummy_read_pixel = 8; 72 | cfg.dummy_read_bits = 1; 73 | cfg.readable = true; 74 | cfg.invert = true; 75 | cfg.rgb_order = false; 76 | cfg.dlen_16bit = false; 77 | cfg.bus_shared = true; 78 | 79 | _panel_instance.config(cfg); 80 | } 81 | 82 | { 83 | auto cfg = _light_instance.config(); 84 | 85 | cfg.pin_bl = 45; 86 | cfg.invert = false; 87 | cfg.freq = 44100; 88 | cfg.pwm_channel = 7; 89 | 90 | _light_instance.config(cfg); 91 | _panel_instance.setLight(&_light_instance); 92 | } 93 | 94 | { 95 | auto cfg = _touch_instance.config(); 96 | 97 | cfg.x_min = 0; 98 | cfg.x_max = screenWidth - 1; 99 | cfg.y_min = 0; 100 | cfg.y_max = screenHeight - 1; 101 | cfg.pin_int = 7; 102 | cfg.bus_shared = true; 103 | cfg.offset_rotation = 0; 104 | // I2C 105 | cfg.i2c_port = 0; 106 | cfg.i2c_addr = 0x38; 107 | cfg.pin_sda = 6; 108 | cfg.pin_scl = 5; 109 | cfg.freq = 400000; 110 | 111 | _touch_instance.config(cfg); 112 | _panel_instance.setTouch(&_touch_instance); 113 | } 114 | 115 | setPanel(&_panel_instance); 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /include/graphics/LVGL/LVGLGraphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lvgl.h" 4 | #include 5 | 6 | class LVGLGraphics 7 | { 8 | public: 9 | LVGLGraphics(uint16_t width, uint16_t height); 10 | void init(void); 11 | 12 | private: 13 | static void lv_debug(lv_log_level_t level, const char *buf); 14 | 15 | uint16_t screenWidth; 16 | uint16_t screenHeight; 17 | }; -------------------------------------------------------------------------------- /include/graphics/common/BatteryLevel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifndef CHARGING_VOLTAGE 6 | #define CHARGING_VOLTAGE 4.30 7 | #endif 8 | 9 | class BatteryLevel 10 | { 11 | public: 12 | enum Status { Plugged, Charging, Full, Mid, Low, Empty, Warn }; 13 | 14 | BatteryLevel(void); 15 | Status calcStatus(uint32_t percentage, float voltage); 16 | 17 | private: 18 | struct { 19 | uint32_t percentage; 20 | float voltage; 21 | } levels[7]; 22 | }; -------------------------------------------------------------------------------- /include/graphics/common/LoRaPresets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mesh-pb-constants.h" 4 | #include 5 | 6 | class LoRaPresets 7 | { 8 | public: 9 | struct RegionInfo { 10 | const char *region; 11 | float freqStart; 12 | float freqEnd; 13 | }; 14 | 15 | static const char *loRaRegionToString(meshtastic_Config_LoRaConfig_RegionCode region); 16 | static float getFrequencyStart(meshtastic_Config_LoRaConfig_RegionCode region); 17 | static float getFrequencyEnd(meshtastic_Config_LoRaConfig_RegionCode region); 18 | static uint16_t getDefaultSlot(meshtastic_Config_LoRaConfig_RegionCode region, 19 | meshtastic_Config_LoRaConfig_ModemPreset preset, const char *channelName = nullptr); 20 | 21 | struct ModemPreset { 22 | const char *preset; 23 | const char *bandwidth_kHz; 24 | float bandwidth_MHz; 25 | }; 26 | 27 | static float getBandwidth(meshtastic_Config_LoRaConfig_ModemPreset preset); 28 | static const char *getBandwidthString(meshtastic_Config_LoRaConfig_ModemPreset preset); 29 | static const char *modemPresetToString(meshtastic_Config_LoRaConfig_ModemPreset preset); 30 | 31 | static uint32_t getNumChannels(meshtastic_Config_LoRaConfig_RegionCode region, 32 | meshtastic_Config_LoRaConfig_ModemPreset preset); 33 | static float getRadioFreq(meshtastic_Config_LoRaConfig_RegionCode region, meshtastic_Config_LoRaConfig_ModemPreset preset, 34 | uint32_t channel); 35 | 36 | private: 37 | static struct RegionInfo regionInfo[]; 38 | static struct ModemPreset modemPreset[]; 39 | }; 40 | -------------------------------------------------------------------------------- /include/graphics/common/ResponseHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief Generic class that stores an id together with a timestamp, cookie and callback function. 9 | * Returns a unique requestId for later reference. 10 | * Callbacks can be used to decouple the reply handler from the actual caller. 11 | */ 12 | class ResponseHandler 13 | { 14 | public: 15 | struct Request; 16 | 17 | enum RequestType { noRequest, TextMessageRequest, TraceRouteRequest, PositionRequest, RemoteConfigRequest, anyRequest }; 18 | enum EventType { found, removed, timeout, user1, user2, user3 }; 19 | 20 | using Callback = std::function; 21 | 22 | struct Request { 23 | uint32_t id; 24 | unsigned long timestamp; 25 | enum RequestType type; 26 | void *cookie; 27 | Callback cb; 28 | }; 29 | 30 | ResponseHandler(uint32_t timeout); 31 | // add new request, pass optional anytype cookie and optional callback function 32 | virtual uint32_t addRequest(uint32_t id, RequestType type, void *cookie = nullptr, Callback cb = nullptr); 33 | // findRequest, call cb on found if pass != -1 and if request type matches 34 | virtual Request findRequest(uint32_t requestId, RequestType match = anyRequest, int32_t pass = -1); 35 | // removeRequest, call cb on found if pass != -1) and if event type matches 36 | virtual Request removeRequest(uint32_t requestId, RequestType match = anyRequest, int32_t pass = -1); 37 | // custom request for implementing user function in derived class 38 | virtual Request customRequest(uint32_t requestId, RequestType match = anyRequest, int32_t pass = -1) { return Request{}; } 39 | // task handler, must be called periodically 40 | virtual void task_handler(void); 41 | 42 | ~ResponseHandler() = default; 43 | 44 | protected: 45 | virtual uint32_t generatePacketId(void); 46 | 47 | static uint32_t rollingPacketId; 48 | uint32_t requestIdCounter; 49 | uint32_t maxTime; 50 | std::unordered_map pendingRequest; 51 | 52 | private: 53 | ResponseHandler(const ResponseHandler &) = delete; 54 | ResponseHandler &operator=(const ResponseHandler &) = delete; 55 | }; -------------------------------------------------------------------------------- /include/graphics/common/Ringtones.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Rtttl { 4 | const char *name; 5 | const char *rtttl; 6 | }; 7 | 8 | extern const Rtttl ringtone[]; 9 | extern const int numRingtones; -------------------------------------------------------------------------------- /include/graphics/common/SdCard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(ARCH_PORTDUINO) 4 | #include "PortduinoFS.h" 5 | extern fs::FS &SDFs; 6 | 7 | #elif defined(HAS_SD_MMC) 8 | #include "SD_MMC.h" // TODO: replace by SdFat SDIO 9 | extern fs::SDMMCFS &SDFs; 10 | 11 | #elif defined(HAS_SDCARD) 12 | #include "SdFat.h" 13 | extern SdFs SDFs; 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | /** 20 | * SdCard adapter to define a common interface for SD and SdFs 21 | */ 22 | class ISdCard 23 | { 24 | public: 25 | enum CardType { 26 | eNone, 27 | eMMC, 28 | eSD, 29 | eSDHC, 30 | eSDXC, 31 | eSDUC, 32 | eUnknown, 33 | }; 34 | 35 | enum FatType { 36 | eNA, 37 | eFat16, 38 | eFat32, 39 | eExFat, 40 | }; 41 | 42 | enum ErrorType { eNoError, eSlotEmpty, eCardError, eFormatError, eNoMbrError, eUnknownError }; 43 | 44 | virtual bool init(void) = 0; 45 | virtual CardType cardType(void) = 0; 46 | virtual FatType fatType(void) = 0; 47 | virtual ErrorType errorType(void) = 0; 48 | 49 | virtual uint64_t usedBytes(void) = 0; 50 | virtual uint64_t freeBytes(void) = 0; 51 | virtual uint64_t cardSize(void) = 0; 52 | virtual bool format(void) = 0; 53 | 54 | bool isUpdated(void) { return updated; } 55 | virtual std::set loadMapStyles(const char *folder) = 0; 56 | virtual ~ISdCard(void) {} 57 | 58 | protected: 59 | bool updated = false; 60 | }; 61 | 62 | #if defined(ARCH_PORTDUINO) || defined(HAS_SD_MMC) 63 | class SDCard : public ISdCard 64 | { 65 | public: 66 | bool init(void) override; 67 | CardType cardType(void) override; 68 | FatType fatType(void) override; 69 | ErrorType errorType(void) override; 70 | uint64_t usedBytes(void) override; 71 | uint64_t freeBytes(void) override; 72 | uint64_t cardSize(void) override; 73 | bool format(void) override { return false; }; 74 | 75 | std::set loadMapStyles(const char *folder) override; 76 | virtual ~SDCard(void); 77 | }; 78 | 79 | #elif defined(HAS_SDCARD) 80 | class SdFsCard : public ISdCard 81 | { 82 | public: 83 | bool init(void) override; 84 | CardType cardType(void) override; 85 | FatType fatType(void) override; 86 | ErrorType errorType(void) override; 87 | uint64_t usedBytes(void) override; 88 | uint64_t freeBytes(void) override; 89 | uint64_t cardSize(void) override; 90 | bool format(void) override; 91 | 92 | std::set loadMapStyles(const char *folder) override; 93 | virtual ~SdFsCard(void) {} 94 | }; 95 | 96 | #endif 97 | 98 | /** 99 | * Dummy to emulate sdCard interface without card reader or without card inserted 100 | */ 101 | class NoSdCard : public ISdCard 102 | { 103 | public: 104 | bool init(void) override { return true; } 105 | CardType cardType(void) override { return CardType::eNone; } 106 | FatType fatType(void) override { return FatType::eNA; } 107 | ErrorType errorType(void) override { return ErrorType::eNoError; } 108 | uint64_t usedBytes(void) override { return 0; } 109 | uint64_t freeBytes(void) override { return 0; } 110 | uint64_t cardSize(void) override { return 1; } 111 | bool format(void) override { return false; } 112 | 113 | std::set loadMapStyles(const char *folder) override 114 | { 115 | updated = true; 116 | return std::set{}; 117 | } 118 | virtual ~NoSdCard(void) {} 119 | }; 120 | 121 | extern ISdCard *sdCard; -------------------------------------------------------------------------------- /include/graphics/common/ViewFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "graphics/driver/DisplayDriverConfig.h" 3 | 4 | class DeviceGUI; 5 | 6 | class ViewFactory 7 | { 8 | public: 9 | static DeviceGUI *create(void); 10 | static DeviceGUI *create(const DisplayDriverConfig &cfg); 11 | 12 | protected: 13 | ViewFactory(void); 14 | }; 15 | -------------------------------------------------------------------------------- /include/graphics/driver/DisplayDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/DeviceGUI.h" 4 | #include "graphics/LVGL/LVGLGraphics.h" 5 | #include 6 | 7 | #define H_NORM_PX(h_scr_percent) ((int16_t)((screenWidth / 100.0) * (h_scr_percent))) 8 | #define V_NORM_PX(v_scr_percent) ((int16_t)((screenHeight / 100.0) * (v_scr_percent))) 9 | 10 | typedef lv_display_t LVGLDisplay; 11 | typedef lv_indev_t LVGLTouch; 12 | 13 | class DisplayDriver 14 | { 15 | public: 16 | DisplayDriver(uint16_t width, uint16_t height); 17 | virtual void init(DeviceGUI *gui); 18 | virtual bool calibrate(uint16_t parameters[8]) { return false; } 19 | virtual bool hasTouch(void) { return false; } 20 | virtual bool hasButton(void) { return false; } 21 | virtual bool hasLight(void) { return false; } 22 | virtual void task_handler(void) { lv_timer_periodic_handler(); }; 23 | virtual bool isPowersaving() { return false; } 24 | virtual void printConfig(void) {} 25 | virtual ~DisplayDriver() {} 26 | 27 | virtual uint8_t getBrightness() { return 255; } 28 | virtual void setBrightness(uint8_t timeout) {} 29 | 30 | virtual uint16_t getScreenTimeout() { return 0; } 31 | virtual void setScreenTimeout(uint16_t timeout) {} 32 | 33 | uint16_t getScreenWidth(void) { return screenWidth; } 34 | uint16_t getScreenHeight(void) { return screenHeight; } 35 | 36 | lv_display_t *getDisplay(void) { return display; } 37 | 38 | protected: 39 | LVGLGraphics lvgl; 40 | LVGLDisplay *display; 41 | LVGLTouch *touch; 42 | DeviceGUI *view; 43 | uint16_t screenWidth; 44 | uint16_t screenHeight; 45 | }; 46 | -------------------------------------------------------------------------------- /include/graphics/driver/DisplayDriverFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "graphics/driver/DisplayDriverConfig.h" 3 | 4 | class DisplayDriver; 5 | 6 | class DisplayDriverFactory 7 | { 8 | public: 9 | static DisplayDriver *create(uint16_t width, uint16_t height); 10 | static DisplayDriver *create(const DisplayDriverConfig &config); 11 | 12 | private: 13 | DisplayDriverFactory(void); 14 | // TODO: DisplayDriverFactory(uint32_t width, uint32_t height); 15 | // TODO: DisplayDriverFactory(const DisplayDriverConfig& config); 16 | }; 17 | -------------------------------------------------------------------------------- /include/graphics/driver/FBDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "graphics/driver/DisplayDriver.h" 3 | 4 | /** 5 | * @brief For simulation on pc/raspberry 6 | * This class provides an FB GUI on the local desktop; dimensions are defined 7 | * in lv_drv_conf.h Usage: define USE_FB=1 for the rasbian/portduino target and 8 | * link with -lFB 9 | */ 10 | class FBDriver : public DisplayDriver 11 | { 12 | public: 13 | static FBDriver &create(uint16_t width, uint16_t height); 14 | void init(DeviceGUI *gui) override; 15 | virtual ~FBDriver() {} 16 | 17 | private: 18 | FBDriver(uint16_t width, uint16_t height); 19 | 20 | #if LV_USE_EVDEV 21 | static void discovery_cb(lv_indev_t *indev, lv_evdev_type_t type, void *user_data); 22 | static void set_mouse_cursor_icon(lv_indev_t *indev, lv_display_t *display); 23 | static void indev_deleted_cb(lv_event_t *e); 24 | #endif 25 | 26 | static FBDriver *fbDriver; 27 | static lv_display_t *display; 28 | }; -------------------------------------------------------------------------------- /include/graphics/driver/OLEDDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/driver/DisplayDriver.h" 4 | 5 | template class OLEDDriver : public DisplayDriver 6 | { 7 | public: 8 | OLEDDriver(OLED *oled, uint16_t width, uint16_t height); 9 | virtual void init(DeviceGUI *gui); 10 | 11 | protected: 12 | OLED *oled; 13 | }; 14 | 15 | template 16 | OLEDDriver::OLEDDriver(OLED *oled, uint16_t width, uint16_t height) : DisplayDriver(width, height), OLED(oled) 17 | { 18 | } 19 | 20 | template void OLEDDriver::init(DeviceGUI *gui) 21 | { 22 | DisplayDriver::init(gui); 23 | } 24 | -------------------------------------------------------------------------------- /include/graphics/driver/TFTDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/driver/DisplayDriver.h" 4 | 5 | template class TFTDriver : public DisplayDriver 6 | { 7 | public: 8 | TFTDriver(TFT *tft, uint16_t width, uint16_t height); 9 | virtual void init(DeviceGUI *gui); 10 | 11 | protected: 12 | TFT *tft; 13 | }; 14 | 15 | template TFTDriver::TFTDriver(TFT *tft, uint16_t width, uint16_t height) : DisplayDriver(width, height), tft(tft) 16 | { 17 | } 18 | 19 | template void TFTDriver::init(DeviceGUI *gui) 20 | { 21 | DisplayDriver::init(gui); 22 | 23 | #ifdef ARCH_ESP32 24 | #ifdef DONT_USE_ESP_TIMER 25 | lv_tick_set_cb(xTaskGetTickCount); 26 | #else 27 | // Create esp timer to call lvgl lv_tick_inc() 28 | const esp_timer_create_args_t lvgl_tick_timer_args = {.callback = [](void *arg) { lv_tick_inc(20); }, .name = "lvgl_tick"}; 29 | esp_timer_handle_t lvgl_tick_timer = nullptr; 30 | ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); 31 | ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 20000)); 32 | #endif 33 | #elif defined(ARCH_PORTDUINO) 34 | // for linux we use lv_tick_inc() in DeviceGUI::task_handler() 35 | // lv_tick_set_cb([]() -> uint32_t { return millis(); }); 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /include/graphics/driver/X11Driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "graphics/driver/DisplayDriver.h" 3 | 4 | /** 5 | * @brief For simulation on pc/raspberry 6 | * This class provides an X11 GUI on the local desktop; dimensions are defined 7 | * in lv_drv_conf.h Usage: define USE_X11=1 for the rasbian/portduino target and 8 | * link with -lX11 9 | */ 10 | class X11Driver : public DisplayDriver 11 | { 12 | public: 13 | static X11Driver &create(uint16_t width, uint16_t height); 14 | void init(DeviceGUI *gui) override; 15 | virtual ~X11Driver() {} 16 | 17 | private: 18 | X11Driver(uint16_t width, uint16_t height); 19 | 20 | static X11Driver *x11driver; 21 | }; -------------------------------------------------------------------------------- /include/graphics/map/FileSystemService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/map/TileService.h" 4 | #include "lvgl.h" 5 | 6 | class FileSystemService : public ITileService 7 | { 8 | public: 9 | FileSystemService(); 10 | 11 | virtual ~FileSystemService(); 12 | 13 | bool load(const char *name, void *img) override; 14 | 15 | protected: 16 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 17 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 18 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 19 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 20 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 21 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 22 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 23 | }; 24 | -------------------------------------------------------------------------------- /include/graphics/map/LinuxFileSystemService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/map/TileService.h" 4 | #include "lvgl.h" 5 | 6 | class LinuxFileSystemService : public ITileService 7 | { 8 | public: 9 | LinuxFileSystemService(); 10 | 11 | virtual ~LinuxFileSystemService(); 12 | 13 | bool load(const char *name, void *img) override; 14 | 15 | protected: 16 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 17 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 18 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 19 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 20 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 21 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 22 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 23 | }; 24 | -------------------------------------------------------------------------------- /include/graphics/map/MapTile.h: -------------------------------------------------------------------------------- 1 | #include "graphics/map/OSMTiles.h" 2 | #include "lvgl.h" 3 | #include "stdint.h" 4 | 5 | /** 6 | * Map tile graphical element. Creates and loads a tile image using the OSMTile provider. 7 | */ 8 | class MapTile : public OSMTiles::Tile 9 | { 10 | public: 11 | MapTile(uint32_t xTile, uint32_t yTile); 12 | bool load(lv_obj_t *p, int16_t posx, int16_t posy, const lv_image_dsc_t *noTile); 13 | bool move(int16_t posx, int16_t posy); 14 | void unload(void); 15 | 16 | int16_t getX(void) const { return x; } 17 | int16_t getY(void) const { return y; } 18 | 19 | void removeImage(void); 20 | ~MapTile(); 21 | 22 | protected: 23 | int16_t x; // x-pos in parent panel 24 | int16_t y; // y-pos in parent panel 25 | lv_obj_t *img; // lvgl tile image 26 | lv_obj_t *lbl; // debug label 27 | }; -------------------------------------------------------------------------------- /include/graphics/map/MapTileSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * Global settings for raster tile map 8 | */ 9 | class MapTileSettings 10 | { 11 | public: 12 | MapTileSettings() = default; 13 | static uint8_t getDefaultZoom(void) { return zoomDefault; } 14 | static void setDefaultZoom(uint8_t zoom) { zoomDefault = zoom; } 15 | 16 | static uint8_t getZoomLevel(void) { return zoomLevel; } 17 | static void setZoomLevel(uint8_t level) { zoomLevel = level; } 18 | 19 | static int16_t getTileSize(void) { return tileSize; } 20 | static void setTileSize(uint16_t size) { tileSize = size; } 21 | 22 | static uint32_t getCacheSize(void) { return cacheSize; } 23 | 24 | static float getDefaultLat(void) { return defaultLat; } 25 | static void setDefaultLat(float lat) { defaultLat = lat; } 26 | 27 | static float getDefaultLon(void) { return defaultLon; } 28 | static void setDefaultLon(float lon) { defaultLon = lon; } 29 | 30 | static const char *getPrefix(void) { return prefix; } 31 | static void setPrefix(const char *p) { strcpy(prefix, p); } 32 | 33 | static const char *getTileStyle(void) { return tileStyle; } 34 | static void setTileStyle(const char *p) 35 | { 36 | strcpy(tileStyle, p); 37 | size_t len = strlen(tileStyle); 38 | if (len > 0 && tileStyle[len - 1] != '/') 39 | strcat(tileStyle, "/"); 40 | } 41 | 42 | static const char *getTileFormat(void) { return tileFormat; } 43 | static void setTileFormat(const char *p) { strcpy(tileFormat, p); } 44 | 45 | static bool getDebug(void) { return debug; } 46 | static void setDebug(bool on) { debug = on; } 47 | 48 | private: 49 | static uint8_t zoomLevel; 50 | static uint8_t zoomDefault; 51 | static uint16_t tileSize; 52 | static uint32_t cacheSize; 53 | static float defaultLat; 54 | static float defaultLon; 55 | static char prefix[]; 56 | static char tileStyle[]; 57 | static char tileFormat[]; 58 | static bool debug; 59 | }; 60 | -------------------------------------------------------------------------------- /include/graphics/map/OSMTiles.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/map/GeoPoint.h" 4 | #include "graphics/map/MapTileSettings.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef IMG_PATH_LEN 11 | #define IMG_PATH_LEN 64 12 | #endif 13 | 14 | /** 15 | * Generic OSM raster tile loader (singleton class). 16 | * Of course can also load other raster styles than just osm, lol. 17 | */ 18 | template class OSMTiles 19 | { 20 | public: 21 | struct Tile : public GeoPoint { 22 | Tile(float lat, float lon, uint8_t zoom) : GeoPoint(lat, lon, zoom) { filename[0] = '\0'; } 23 | Tile(uint32_t xTile, uint32_t yTile, uint8_t zoom) : GeoPoint(xTile, yTile, zoom) { filename[0] = '\0'; } 24 | char filename[IMG_PATH_LEN]; 25 | }; 26 | 27 | // create instance of this class and provide cb function for loading images 28 | static OSMTiles *create(std::function cb); 29 | 30 | // filename caching for GeoPoint tile 31 | bool load(OSMTiles::Tile &tile, IMG *img) 32 | { 33 | if (!tile.filename[0]) { 34 | std::snprintf(tile.filename, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", MapTileSettings::getPrefix(), 35 | MapTileSettings::getTileStyle(), tile.zoomLevel, tile.xTile, tile.yTile, 36 | MapTileSettings::getTileFormat()); 37 | } 38 | return loadcb(tile.filename, img); 39 | } 40 | 41 | protected: 42 | bool load(const GeoPoint &tile, IMG *img) 43 | { 44 | char name[IMG_PATH_LEN]; 45 | std::snprintf(name, IMG_PATH_LEN, "%s/%s%d/%d/%d.%s", MapTileSettings::getPrefix(), MapTileSettings::getTileStyle(), 46 | tile.zoomLevel, tile.xTile, tile.yTile, MapTileSettings::getTileFormat()); 47 | return loadcb(name, img); 48 | } 49 | 50 | private: 51 | OSMTiles() = default; 52 | static std::function loadcb; 53 | }; 54 | 55 | template OSMTiles *OSMTiles::create(std::function cb) 56 | { 57 | loadcb = cb; 58 | return new OSMTiles; 59 | } 60 | 61 | template std::function OSMTiles::loadcb; 62 | -------------------------------------------------------------------------------- /include/graphics/map/SDCardService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FS.h" 4 | #include "graphics/map/TileService.h" 5 | #include "lvgl.h" 6 | #include 7 | #include 8 | 9 | class SDCardService : public ITileService 10 | { 11 | public: 12 | SDCardService(); 13 | 14 | virtual ~SDCardService(); 15 | 16 | bool load(const char *name, void *img) override; 17 | 18 | protected: 19 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 20 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 21 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 22 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 23 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 24 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 25 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 26 | static void *fs_dir_open(lv_fs_drv_t *drv, const char *path); 27 | static lv_fs_res_t fs_dir_read(lv_fs_drv_t *drv, void *rddir_p, char *fn, uint32_t fn_len); 28 | static lv_fs_res_t fs_dir_close(lv_fs_drv_t *drv, void *rddir_p); 29 | 30 | private: 31 | typedef struct SdFile { 32 | File file; 33 | } SdFile; 34 | }; 35 | -------------------------------------------------------------------------------- /include/graphics/map/SdFatService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SdFat.h" 4 | #include "graphics/map/TileService.h" 5 | #include "lvgl.h" 6 | #include 7 | #include 8 | 9 | class SdFatService : public ITileService 10 | { 11 | public: 12 | SdFatService(); 13 | 14 | virtual ~SdFatService(); 15 | 16 | bool load(const char *name, void *img) override; 17 | 18 | protected: 19 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 20 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 21 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 22 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 23 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 24 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 25 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 26 | static void *fs_dir_open(lv_fs_drv_t *drv, const char *path); 27 | static lv_fs_res_t fs_dir_read(lv_fs_drv_t *drv, void *rddir_p, char *fn, uint32_t fn_len); 28 | static lv_fs_res_t fs_dir_close(lv_fs_drv_t *drv, void *rddir_p); 29 | 30 | private: 31 | typedef struct SdFile { 32 | FsFile file; 33 | } SdFile; 34 | }; 35 | -------------------------------------------------------------------------------- /include/graphics/map/TileService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * Abstract TileService interface; load tile from any source 7 | */ 8 | class ITileService 9 | { 10 | public: 11 | // lvgl lv_fs_drv callbacks 12 | virtual bool load(const char *name, void *img) = 0; 13 | virtual ~ITileService() {} 14 | 15 | protected: 16 | ITileService(const char *id) : idLetter(id){}; 17 | 18 | const char *idLetter; // LVGL letter for file system drives 19 | }; 20 | 21 | /** 22 | * Envelope class to allow runtime configuration of TileService variants 23 | * Note: This class will delete unused TileService objects. 24 | */ 25 | class TileService : public ITileService 26 | { 27 | public: 28 | TileService(ITileService *s) : ITileService(""), service(s) {} 29 | virtual void setService(ITileService *s); 30 | virtual void setBackupService(ITileService *s); 31 | 32 | bool load(const char *name, void *img) override 33 | { 34 | return service ? service->load(name, img) : backup ? backup->load(name, img) : false; 35 | } 36 | 37 | virtual ~TileService(); 38 | 39 | protected: 40 | ITileService *service = nullptr; 41 | ITileService *backup = nullptr; 42 | }; 43 | -------------------------------------------------------------------------------- /include/graphics/map/URLService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/map/TileService.h" 4 | #include "lvgl.h" 5 | 6 | class URLService : public ITileService 7 | { 8 | public: 9 | URLService(); 10 | 11 | virtual ~URLService(); 12 | 13 | bool load(const char *name, void *img) override; 14 | 15 | protected: 16 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 17 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 18 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 19 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 20 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 21 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 22 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 23 | }; 24 | -------------------------------------------------------------------------------- /include/graphics/view/OLED/OLEDViewController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comms/IClientBase.h" 4 | #include "graphics/common/ViewController.h" 5 | 6 | class DeviceGUI; 7 | 8 | class OLEDViewController : public ViewController 9 | { 10 | public: 11 | OLEDViewController(); 12 | virtual void init(MeshtasticView *gui, IClientBase *_client); 13 | virtual void runOnce(void){}; 14 | virtual ~OLEDViewController(){}; 15 | }; 16 | -------------------------------------------------------------------------------- /include/graphics/view/OLED/OLEDView_128x64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/common/MeshtasticView.h" 4 | #include "graphics/driver/DisplayDriverConfig.h" 5 | 6 | /** 7 | * @brief GUI view for T-Watch-S3 8 | * Handles creation of display driver and controller. 9 | * Note: due to static callbacks in lvgl this class is modelled as 10 | * a singleton with static callback members 11 | */ 12 | class OLEDView_128x64 : public MeshtasticView 13 | { 14 | public: 15 | void init(IClientBase *client) override; 16 | void task_handler(void) override; 17 | 18 | void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) override {} 19 | void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, eRole role, 20 | bool hasKey, bool unmessagable) override 21 | { 22 | } 23 | void updateNode(uint32_t nodeNum, uint8_t channel, const meshtastic_User &cfg) override {} 24 | 25 | protected: 26 | virtual void addMessage(char *msg) {} 27 | virtual void newMessage(uint32_t nodeNum, lv_obj_t *container, uint8_t channel, const char *msg) {} 28 | 29 | private: 30 | // view creation only via ViewFactory 31 | friend class ViewFactory; 32 | static OLEDView_128x64 *instance(void); 33 | static OLEDView_128x64 *instance(const DisplayDriverConfig &cfg); 34 | OLEDView_128x64(); 35 | OLEDView_128x64(const DisplayDriverConfig *cfg, DisplayDriver *driver); 36 | 37 | static OLEDView_128x64 *gui; 38 | }; 39 | -------------------------------------------------------------------------------- /include/graphics/view/TFT/TFTView_160x80.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/common/MeshtasticView.h" 4 | 5 | /** 6 | * @brief GUI view for e.g. Heltec-Tracker 7 | * Handles creation of display driver and controller. 8 | * Note: due to static callbacks in lvgl this class is modelled as 9 | * a singleton with static callback members 10 | */ 11 | class TFTView_160x80 : public MeshtasticView 12 | { 13 | public: 14 | void init(IClientBase *client) override; 15 | void task_handler(void) override; 16 | 17 | void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) override {} 18 | void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, eRole role, 19 | bool hasKey, bool viaMqtt) override 20 | { 21 | } 22 | void updateNode(uint32_t nodeNum, uint8_t channel, const meshtastic_User &cfg) override {} 23 | 24 | protected: 25 | virtual void addMessage(char *msg) {} 26 | virtual void newMessage(uint32_t nodeNum, lv_obj_t *container, uint8_t channel, const char *msg) {} 27 | 28 | private: 29 | // view creation only via ViewFactory 30 | friend class ViewFactory; 31 | static TFTView_160x80 *instance(void); 32 | static TFTView_160x80 *instance(const DisplayDriverConfig &cfg); 33 | TFTView_160x80(); 34 | TFTView_160x80(const DisplayDriverConfig *cfg, DisplayDriver *driver); 35 | 36 | static TFTView_160x80 *gui; 37 | }; -------------------------------------------------------------------------------- /include/graphics/view/TFT/TFTView_240x240.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/common/MeshtasticView.h" 4 | 5 | /** 6 | * @brief GUI view for e.g. T-Watch-S3 7 | * Handles creation of display driver and controller. 8 | * Note: due to static callbacks in lvgl this class is modelled as 9 | * a singleton with static callback members 10 | */ 11 | class TFTView_240x240 : public MeshtasticView 12 | { 13 | public: 14 | void init(IClientBase *client) override; 15 | void task_handler(void) override; 16 | 17 | void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) override {} 18 | void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, eRole role, 19 | bool hasKey, bool unmessagable) override 20 | { 21 | } 22 | void updateNode(uint32_t nodeNum, uint8_t channel, const meshtastic_User &cfg) override {} 23 | 24 | protected: 25 | virtual void addMessage(char *msg) {} 26 | virtual void newMessage(uint32_t nodeNum, lv_obj_t *container, uint8_t channel, const char *msg) {} 27 | 28 | private: 29 | // view creation only via ViewFactory 30 | friend class ViewFactory; 31 | static TFTView_240x240 *instance(void); 32 | static TFTView_240x240 *instance(const DisplayDriverConfig &cfg); 33 | TFTView_240x240(); 34 | TFTView_240x240(const DisplayDriverConfig *cfg, DisplayDriver *driver); 35 | 36 | void ui_events_init(void); 37 | void ui_set_active(lv_obj_t *b, lv_obj_t *p, lv_obj_t *tp); 38 | 39 | // lvgl event callbacks 40 | // static void ui_event_HomeButton(lv_event_t * e); 41 | static void ui_event_NodesButton(lv_event_t *e); 42 | static void ui_event_GroupsButton(lv_event_t *e); 43 | static void ui_event_MessagesButton(lv_event_t *e); 44 | static void ui_event_SettingsButton(lv_event_t *e); 45 | 46 | static void ui_event_NodeButton(lv_event_t *e); 47 | static void ui_event_ChannelButton(lv_event_t *e); 48 | static void ui_event_Keyboard(lv_event_t *e); 49 | 50 | lv_obj_t *activeButton = nullptr; 51 | lv_obj_t *activePanel = nullptr; 52 | lv_obj_t *activeTopPanel = nullptr; 53 | 54 | static TFTView_240x240 *gui; 55 | }; 56 | -------------------------------------------------------------------------------- /include/graphics/view/TFT/TFTView_480x320.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/common/MeshtasticView.h" 4 | 5 | /** 6 | * @brief GUI view for e.g. unPhone or WT32-SC01 Plus 7 | * Handles creation of display driver and controller. 8 | * Note: due to static callbacks in lvgl this class is modelled as 9 | * a singleton with static callback members 10 | */ 11 | class TFTView_480x320 : public MeshtasticView 12 | { 13 | public: 14 | void init(IClientBase *client) override; 15 | void task_handler(void) override; 16 | 17 | void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) override {} 18 | void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, eRole role, 19 | bool hasKey, bool viaMqtt) override 20 | { 21 | } 22 | void updateNode(uint32_t nodeNum, uint8_t channel, const meshtastic_User &cfg) override {} 23 | 24 | protected: 25 | virtual void addMessage(char *msg) {} 26 | virtual void newMessage(uint32_t nodeNum, lv_obj_t *container, uint8_t channel, const char *msg) {} 27 | 28 | private: 29 | // view creation only via ViewFactory 30 | friend class ViewFactory; 31 | static TFTView_480x320 *instance(void); 32 | static TFTView_480x320 *instance(const DisplayDriverConfig &cfg); 33 | TFTView_480x320(); 34 | TFTView_480x320(const DisplayDriverConfig *cfg, DisplayDriver *driver); 35 | 36 | static TFTView_480x320 *gui; 37 | }; -------------------------------------------------------------------------------- /include/graphics/view/TFT/Themes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lvgl.h" 4 | class Themes 5 | { 6 | public: 7 | enum Theme { eDark, eLight, eRed }; 8 | 9 | static void initStyles(void); 10 | static enum Theme get(void); 11 | static void set(enum Theme th); 12 | static void recolorButton(lv_obj_t *obj, bool enabled, lv_opa_t opa = 255); 13 | static void recolorText(lv_obj_t *obj, bool enabled); 14 | static void recolorTopLabel(lv_obj_t *obj, bool alert); 15 | static void recolorTableRow(lv_draw_fill_dsc_t *fill_draw_dsc, bool odd); 16 | 17 | private: 18 | Themes(void) = default; 19 | }; -------------------------------------------------------------------------------- /include/input/ButtonInputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input/InputDriver.h" 4 | 5 | /** 6 | * Button (if available) is used to wake up from display sleep 7 | */ 8 | class ButtonInputDriver : public InputDriver 9 | { 10 | public: 11 | ButtonInputDriver(void); 12 | 13 | virtual void init(void) override; 14 | virtual ~ButtonInputDriver(void) {} 15 | 16 | private: 17 | static void button_read(lv_indev_t *indev, lv_indev_data_t *data); 18 | static int8_t getButtonPressedId(void); 19 | }; -------------------------------------------------------------------------------- /include/input/EncoderInputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input/InputDriver.h" 4 | 5 | class EncoderInputDriver : public InputDriver 6 | { 7 | public: 8 | EncoderInputDriver(void); 9 | 10 | virtual void init(void) override; 11 | virtual ~EncoderInputDriver(void) {} 12 | 13 | static void intPressHandler(void); 14 | static void intDownHandler(void); 15 | static void intUpHandler(void); 16 | static void intLeftHandler(void); 17 | static void intRightHandler(void); 18 | 19 | protected: 20 | enum EncoderActionType { TB_ACTION_NONE, TB_ACTION_PRESSED, TB_ACTION_UP, TB_ACTION_DOWN, TB_ACTION_LEFT, TB_ACTION_RIGHT }; 21 | 22 | static volatile EncoderActionType action; 23 | 24 | static void encoder_read(lv_indev_t *indev, lv_indev_data_t *data); 25 | }; -------------------------------------------------------------------------------- /include/input/I2CKeyboardInputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input/InputDriver.h" 4 | 5 | class I2CKeyboardInputDriver : public InputDriver 6 | { 7 | public: 8 | I2CKeyboardInputDriver(void); 9 | virtual void init(void) override; 10 | virtual void task_handler(void) override; 11 | virtual ~I2CKeyboardInputDriver(void); 12 | 13 | private: 14 | static void keyboard_read(lv_indev_t *indev, lv_indev_data_t *data); 15 | }; -------------------------------------------------------------------------------- /include/input/InputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lvgl.h" 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief This base class merges all input devices of its children 9 | * into one "set". It allows to create several instances of different(!) 10 | * input devices types that are accessible through this common base class. 11 | * There is one global input group creates that serves all input devices. This 12 | * input group behaves according the grid/flex layout of all created widgets. 13 | * The view just needs to apply the focus to the appropriate widget when a panel 14 | * is arranged into forground or background. 15 | */ 16 | class InputDriver 17 | { 18 | public: 19 | static InputDriver *instance(void); 20 | virtual void init(void) {} 21 | virtual void task_handler(void) {} 22 | virtual ~InputDriver(void); 23 | 24 | virtual std::vector getKeyboardDevices(void) { return std::vector(); } 25 | virtual std::vector getPointerDevices(void) { return std::vector(); } 26 | 27 | std::string getCurrentKeyboardDevice(void) { return keyboardDevice; } 28 | std::string getCurrentPointerDevice(void) { return pointerDevice; } 29 | 30 | virtual bool useKeyboardDevice(const std::string &name) { return false; } 31 | virtual bool usePointerDevice(const std::string &name) { return false; } 32 | 33 | virtual bool releaseKeyboardDevice(void) { return keyboard == nullptr; } 34 | virtual bool releasePointerDevice(void) { return pointer == nullptr; } 35 | 36 | virtual bool hasKeyboardDevice(void) { return keyboard != nullptr; } 37 | virtual bool hasPointerDevice(void) { return pointer != nullptr; } 38 | virtual bool hasEncoderDevice(void) { return encoder != nullptr; } 39 | virtual bool hasButtonDevice(void) { return button != nullptr; } 40 | 41 | virtual lv_indev_t *getKeyboard(void) { return keyboard; } 42 | virtual lv_indev_t *getPointer(void) { return pointer; } 43 | virtual lv_indev_t *getEncoder(void) { return encoder; } 44 | virtual lv_indev_t *getButton(void) { return button; } 45 | 46 | static lv_group_t *getInputGroup(void) { return inputGroup; } 47 | 48 | protected: 49 | InputDriver(void) : keyboardDevice("none"), pointerDevice("none") {} 50 | static InputDriver *driver; 51 | static lv_indev_t *keyboard; 52 | static lv_indev_t *pointer; 53 | static lv_indev_t *encoder; 54 | static lv_indev_t *button; 55 | static lv_group_t *inputGroup; 56 | 57 | // used for linux hot plugging and unplugging 58 | std::string keyboardDevice; // current keyboard device string in use 59 | std::string pointerDevice; // current pointer device string in use 60 | }; 61 | -------------------------------------------------------------------------------- /include/input/KeyMatrixInputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input/InputDriver.h" 4 | 5 | class KeyMatrixInputDriver : public InputDriver 6 | { 7 | public: 8 | KeyMatrixInputDriver(void); 9 | 10 | virtual void init(void) override; 11 | virtual ~KeyMatrixInputDriver(void) {} 12 | 13 | private: 14 | static void keyboard_read(lv_indev_t *indev, lv_indev_data_t *data); 15 | }; -------------------------------------------------------------------------------- /include/input/LinuxInputDriver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input/InputDriver.h" 4 | 5 | class LinuxInputDriver : public InputDriver 6 | { 7 | public: 8 | LinuxInputDriver(const std::string &kbdDevice, const std::string &ptrDevice); 9 | void init(void) override; 10 | void task_handler(void) override; 11 | virtual ~LinuxInputDriver(void); 12 | 13 | std::vector getKeyboardDevices(void) override; 14 | std::vector getPointerDevices(void) override; 15 | 16 | std::string getCurrentKeyboardDevice(void) { return keyboardDevice; } 17 | std::string getCurrentPointerDevice(void) { return pointerDevice; } 18 | 19 | bool useKeyboardDevice(const std::string &name) override; 20 | bool usePointerDevice(const std::string &name) override; 21 | 22 | bool releaseKeyboardDevice(void) override; 23 | bool releasePointerDevice(void) override; 24 | 25 | private: 26 | std::vector globVector(const std::string &pattern); 27 | lv_obj_t *mouse_cursor = nullptr; 28 | }; 29 | -------------------------------------------------------------------------------- /include/util/FileLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FS.h" 4 | #include "lvgl.h" 5 | 6 | #define FL_DRIVE_LETTER "L:" 7 | 8 | class FileLoader 9 | { 10 | public: 11 | FileLoader(){}; 12 | static void init(fs::FS *fs); 13 | static bool loadImage(lv_obj_t *img, const char *path); 14 | static bool loadBootImage(lv_obj_t *img); 15 | 16 | private: 17 | static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode); 18 | static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p); 19 | static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br); 20 | static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw); 21 | static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence); 22 | static lv_fs_res_t fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p); 23 | static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p); 24 | 25 | typedef struct FileHandle { 26 | File file; 27 | } FileHandle; 28 | 29 | static fs::FS *_fs; 30 | }; 31 | -------------------------------------------------------------------------------- /include/util/ILogEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * Generic interface base class for any log entries (stored via class LogRotate) 8 | */ 9 | class ILogEntry 10 | { 11 | public: 12 | virtual size_t size(void) const = 0; 13 | virtual size_t serialize(std::function write) const = 0; 14 | virtual size_t deserialize(std::function read) = 0; 15 | virtual ~ILogEntry() = default; 16 | 17 | protected: 18 | ILogEntry(void) = default; 19 | 20 | private: 21 | ILogEntry(const ILogEntry &) = delete; 22 | ILogEntry &operator=(const ILogEntry &) = delete; 23 | }; 24 | -------------------------------------------------------------------------------- /include/util/LinuxHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class LinuxHelper 6 | { 7 | public: 8 | static uint32_t getAvailableMem(void); 9 | static uint32_t getFreeMem(void); 10 | static uint32_t getTotalMem(void); 11 | 12 | protected: 13 | static uint32_t getMem(const char *entry); 14 | }; -------------------------------------------------------------------------------- /include/util/LogMessage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ILogEntry.h" 4 | #include 5 | #include 6 | #include 7 | 8 | constexpr uint32_t messagePayloadSize = 233; 9 | 10 | /** 11 | * @brief Header for storing message logs containing the actual size of the payload 12 | * Note: this struct does have vtable pointers, i.e. sizeof(LogMessageHeader)-8 is the real data size 13 | */ 14 | struct LogMessageHeader : public ILogEntry { 15 | uint16_t _size; 16 | time_t time; 17 | uint32_t from; 18 | uint32_t to; 19 | uint8_t ch; 20 | enum MsgStatus : uint8_t { eNone, eDefault, eHeard, eNoResponse, eAcked, eFailed, eDeleted, eUnread } status; 21 | bool trashFlag; 22 | uint32_t reserved; 23 | }; 24 | 25 | /** 26 | * @brief Structure for storing message logs containing the actual payload 27 | */ 28 | struct LogMessage : public LogMessageHeader { 29 | uint8_t bytes[messagePayloadSize]; 30 | }; 31 | 32 | /** 33 | * Log message envelope that implements the actual interface for ILogEntry 34 | * (size, serialize and deserialize) 35 | */ 36 | class LogMessageEnv : public LogMessage 37 | { 38 | public: 39 | LogMessageEnv(void) = default; 40 | LogMessageEnv(uint32_t _from, uint32_t _to, uint16_t _ch, time_t _time, MsgStatus _status, bool _trashFlag, uint32_t _len, 41 | const uint8_t *msg) 42 | { 43 | assert(_len < messagePayloadSize); 44 | _size = (uint16_t)_len; 45 | time = _time; 46 | from = _from; 47 | to = _to; 48 | ch = _ch; 49 | status = _status; 50 | trashFlag = _trashFlag; 51 | reserved = 0; 52 | memcpy(bytes, msg, _len); 53 | } 54 | 55 | size_t size(void) const override { return sizeof(LogMessageHeader) - 8 + _size; } 56 | 57 | virtual size_t serialize(std::function write) const override 58 | { 59 | return write((uint8_t *)&_size, sizeof(LogMessageHeader) - 8) + write(bytes, _size); 60 | } 61 | 62 | virtual size_t deserialize(std::function read) override 63 | { 64 | return read((uint8_t *)&_size, sizeof(LogMessageHeader) - 8) + read(bytes, _size); 65 | } 66 | }; -------------------------------------------------------------------------------- /include/util/LogRotate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FS.h" 4 | #include "ILogEntry.h" 5 | #include 6 | 7 | /** 8 | * Generic LogRotate class that writes log-rotation like files into (arduino) FS storage file system 9 | * @param fs arduino file system FS/LittleFS or derived classes 10 | * @param logDir directory to store the logs (absolute path) 11 | * @param maxLen the maximum length of the variable log entry length 12 | * The maximum storage is limited by: 13 | * @param maxSize the total storage in bytes (default is 200kB) 14 | * @param maxFiles number of log files (default is 50) 15 | * @param maxFileSize per log file (default size is 4000 bytes to fit into a physical block 16 | * including fs descriptor data) 17 | * 18 | * If the maximum storage is exceeded then old files are deleted to fit the new log entry. 19 | * Note: for performance reasons the logs are not renumbered 20 | */ 21 | class LogRotate 22 | { 23 | public: 24 | LogRotate(fs::FS &fs, const char *logDir, uint32_t maxLen, uint32_t maxSize = 102400, uint32_t maxFiles = 25, 25 | uint32_t maxFileSize = 4000); 26 | // uint32_t maxSize = 4096, uint32_t maxFiles = 10, uint32_t maxFileSize = 400); 27 | 28 | // initialize the log directory 29 | void init(void); 30 | // write a log entry to fs 31 | bool write(const ILogEntry &entry); 32 | // read the next log entry from fs 33 | bool readNext(ILogEntry &entry); 34 | // remove all logs from fs 35 | bool clear(void); 36 | 37 | // request total size of logs 38 | uint32_t size(void) const; 39 | // request log count 40 | uint32_t count(void) const; 41 | // request current log number 42 | uint32_t current(void) const; 43 | 44 | private: 45 | LogRotate(const LogRotate &) = delete; 46 | LogRotate &operator=(const LogRotate &) = delete; 47 | 48 | // create filename from number 49 | String logFileName(uint32_t num); 50 | // remove oldest log and return freed size 51 | size_t removeLog(void); 52 | // scan all files in logdir to get min/max log 53 | void scanLogDir(uint32_t &num, uint32_t &minLog, uint32_t &maxLog, uint32_t &size, uint32_t &total); 54 | 55 | const uint32_t c_maxLen; // maximum size a single log entry could be 56 | const uint32_t c_maxSize; // max storage size in bytes (default is 100kB) 57 | const uint32_t c_maxFiles; // max log files number (default is 50) 58 | const uint32_t c_maxFileSize; // max file size per log file 59 | 60 | fs::FS &_fs; 61 | File rootDir; // directory (for reading logs) 62 | File currentFile; // current file (when reading) 63 | String rootDirName; // path of log directory 64 | String currentLogName; // current log file name (when writing) 65 | uint32_t numFiles; // number of log files 66 | uint32_t minLogNum; // logfile with smallest number 67 | uint32_t maxLogNum; // logfile with largest number after init() 68 | uint32_t currentLogRead; // current log number (when reading) 69 | uint32_t currentLogWrite; // current log number (when writing) 70 | uint32_t currentSize; // size of current written log file 71 | uint32_t totalSize; // size of all logs 72 | }; -------------------------------------------------------------------------------- /include/util/Packet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * Polymorphic packets that can be moved into and out of packet queues. 7 | */ 8 | class Packet 9 | { 10 | public: 11 | using PacketPtr = std::unique_ptr; 12 | 13 | Packet(int packetId) : id(packetId) {} 14 | 15 | // virtual move constructor 16 | virtual PacketPtr move() { return PacketPtr(new Packet(std::move(*this))); } 17 | 18 | // Disable copying 19 | Packet(const Packet &) = delete; 20 | Packet &operator=(const Packet &) = delete; 21 | 22 | virtual ~Packet() {} 23 | 24 | int getPacketId() const { return id; } 25 | 26 | protected: 27 | // Enable moving 28 | Packet(Packet &&) = default; 29 | Packet &operator=(Packet &&) = default; 30 | 31 | private: 32 | int id; 33 | }; 34 | 35 | /** 36 | * generic packet type class 37 | */ 38 | template class DataPacket : public Packet 39 | { 40 | public: 41 | template DataPacket(int id, Args &&...args) : Packet(id), data(new PacketType(std::forward(args)...)) 42 | { 43 | } 44 | 45 | PacketPtr move() override { return PacketPtr(new DataPacket(std::move(*this))); } 46 | 47 | // Disable copying 48 | DataPacket(const DataPacket &) = delete; 49 | DataPacket &operator=(const DataPacket &) = delete; 50 | 51 | virtual ~DataPacket() {} 52 | 53 | const PacketType &getData() const { return *data; } 54 | 55 | protected: 56 | // Enable moving 57 | DataPacket(DataPacket &&) = default; 58 | DataPacket &operator=(DataPacket &&) = default; 59 | 60 | private: 61 | std::unique_ptr data; 62 | }; 63 | -------------------------------------------------------------------------------- /include/util/PacketQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef BLOCKING_PACKET_QUEUE 8 | #include 9 | #endif 10 | 11 | /** 12 | * Generic platform independent and re-entrant queue wrapper that can be used to 13 | * safely pass (generic) movable objects between threads. 14 | */ 15 | template class PacketQueue 16 | { 17 | public: 18 | PacketQueue() {} 19 | 20 | PacketQueue(PacketQueue const &other) = delete; 21 | 22 | /** 23 | * Push movable object into queue 24 | */ 25 | void push(T &&packet) 26 | { 27 | std::lock_guard lock(mutex); 28 | queue.push(packet.move()); 29 | #ifdef BLOCKING_PACKET_QUEUE 30 | cond.notify_one(); 31 | #endif 32 | } 33 | 34 | #ifdef BLOCKING_PACKET_QUEUE 35 | /** 36 | * Pop movable object from queue (blocking) 37 | */ 38 | std::unique_ptr pop(void) 39 | { 40 | std::unique_lock lock(mutex); 41 | cond.wait(lock, [this] { return !queue.empty(); }); 42 | T packet = queue.front()->move(); 43 | queue.pop(); 44 | return packet; 45 | } 46 | #endif 47 | 48 | /** 49 | * Pop movable object from queue (non-blocking) 50 | */ 51 | std::unique_ptr try_pop() 52 | { 53 | std::lock_guard lock(mutex); 54 | if (queue.empty()) 55 | return {nullptr}; 56 | auto packet = queue.front()->move(); 57 | queue.pop(); 58 | return packet; 59 | } 60 | 61 | uint32_t size() const 62 | { 63 | std::lock_guard lock(mutex); 64 | return queue.size(); 65 | } 66 | 67 | private: 68 | mutable std::mutex mutex; 69 | std::queue> queue; 70 | #ifdef BLOCKING_PACKET_QUEUE 71 | std::condition_variable cond; 72 | #endif 73 | }; -------------------------------------------------------------------------------- /include/util/README.md: -------------------------------------------------------------------------------- 1 | Note: files in this folder MUST NOT have any kind of dependency to any other files outside of this directory!! 2 | Goal is to put all files of this directory into a separate library for re-use in other projects. 3 | -------------------------------------------------------------------------------- /include/util/SharedQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Packet.h" 4 | #include "PacketQueue.h" 5 | 6 | /** 7 | * @brief Queue wrapper that aggregates two thread queues (namely client and server) 8 | * for bidirectional packet transfer between two threads or processes. 9 | * 10 | * This queue may also be created in shared memory (e.g. in Linux for inter-process communication) 11 | */ 12 | class SharedQueue 13 | { 14 | public: 15 | SharedQueue(); 16 | virtual ~SharedQueue(); 17 | 18 | // server methods 19 | virtual bool serverSend(Packet &&p); 20 | virtual Packet::PacketPtr serverReceive(); 21 | virtual size_t serverQueueSize() const; 22 | 23 | // client methods 24 | virtual bool clientSend(Packet &&p); 25 | virtual Packet::PacketPtr clientReceive(); 26 | virtual size_t clientQueueSize() const; 27 | 28 | private: 29 | // the server pushes into serverQueue and the client pushes into clientQueue 30 | // receiving is done from the opposite queue, respectively 31 | PacketQueue serverQueue; 32 | PacketQueue clientQueue; 33 | }; 34 | 35 | extern SharedQueue *sharedQueue; -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", 3 | "name": "meshtastic-device-ui", 4 | "version": "1.0.0", 5 | "description": "LVGL GUI for meshtastic devices", 6 | "keywords": "meshtastic, sensecap, tdeck, twatch, lvgl, tft, lovyangfx, embedded", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/meshtastic/device-ui.git" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "mverch67", 14 | "maintainer": true 15 | } 16 | ], 17 | "license": "GPL-3.0", 18 | "homepage": "https://meshtastic.org/", 19 | "frameworks": "Arduino, FreeRTOS", 20 | "platforms": "Expressif 32, Nordic nRF52, Raspberry Pi RP2040, Native, Linux x86_64, Linux ARM", 21 | "headers": ["DeviceScreen.h", "SharedQueue.h"], 22 | "dependencies": { 23 | "ArduinoThread": "https://github.com/meshtastic/ArduinoThread/archive/7c3ee9e1951551b949763b1f5280f8db1fa4068d.zip", 24 | "lvgl/lvgl": "https://github.com/lvgl/lvgl/archive/9c043167685fc08fbcd30ddf2c285ea1089be82d.zip", 25 | "greiman/SdFat": "https://github.com/mverch67/SdFat/archive/e25bebdc41523eaee6aef164a97df25f7ee59494.zip", 26 | "nanopb/Nanopb": "0.4.91" 27 | }, 28 | "build": { 29 | "libArchive": true, 30 | "extraScript": "extra_script.py", 31 | "includeDir": ".", 32 | "flags": [ 33 | "-Iinclude", 34 | "-Igenerated", 35 | "-Ilocale", 36 | "-DLV_LVGL_H_INCLUDE_SIMPLE", 37 | "-DLV_CONF_INCLUDE_SIMPLE", 38 | "-DLV_COMP_CONF_INCLUDE_SIMPLE" 39 | ], 40 | "srcDir": "." 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /locale/README.md: -------------------------------------------------------------------------------- 1 | To install: 2 | 3 | ``` 4 | npm i lv_i18n -g 5 | ``` 6 | 7 | To extract: 8 | 9 | ``` 10 | lv_i18n extract -s 'generated/**/*.+(c|cpp|h|hpp)' -s 'source/**/*.+(c|cpp|h|hpp)' -t 'locale/*.yml' 11 | ``` 12 | 13 | To compile: 14 | 15 | ``` 16 | cd locale 17 | npx lv_i18n compile -t './*.yml' -o '.' 18 | ``` 19 | -------------------------------------------------------------------------------- /locale/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | no new messages: ~ 3 | 1 of 1 nodes online: ~ 4 | uptime 00:00:00: ~ 5 | LoRa 0.0 MHz: ~ 6 | no signal: ~ 7 | silent: ~ 8 | no SD card detected: ~ 9 | "Heap: 0\nLVGL: 0": ~ 10 | DEL: ~ 11 | map tiles not found!: ~ 12 | Settings: ~ 13 | 'User name: ': ~ 14 | 'Modem Preset: LONG FAST': ~ 15 | 'Channel: LongFast': ~ 16 | 'Role: Client': ~ 17 | 'WiFi: ': ~ 18 | 'Screen Timeout: 60s': ~ 19 | 'Lock: off/off': ~ 20 | 'Screen Brightness: 60%': ~ 21 | 'Theme: Dark': ~ 22 | 'Screen Calibration: default': ~ 23 | 'Input Control: none/none': ~ 24 | 'Message Alert Buzzer: on': ~ 25 | 'Language: English': ~ 26 | Configuration Reset: ~ 27 | Backup & Restore: ~ 28 | Reboot / Shutdown: ~ 29 | Tools: ~ 30 | Mesh Detector: ~ 31 | Signal Scanner: ~ 32 | Trace Route: ~ 33 | Neighbors: ~ 34 | Statistics: ~ 35 | Packet Log: ~ 36 | Meshtastic: ~ 37 | Group Channels: ~ 38 | no messages: ~ 39 | Settings & Tools: ~ 40 | Settings (advanced): ~ 41 | Locations Map: ~ 42 | no chats: ~ 43 | Node Search: ~ 44 | Packet Statistics: ~ 45 | Node Options: ~ 46 | LoRa TX off!: ~ 47 | Short Name: ~ 48 | Long Name: ~ 49 | Primary Channel: ~ 50 | : ~ 51 | Secondary Channels: ~ 52 | "LONG FAST\nLONG SLOW\n-- deprecated --\nMEDIUM SLOW\nMEDIUM FAST\nSHORT SLOW\nSHORT FAST\nLONG MODERATE\nSHORT TURBO": ~ 53 | "Client\nClient Mute\nRouter\nRepeater\nTracker\nSensor\nTAK\nClient Hidden\nLost & Found\nTAK Tracker": ~ 54 | WiFi SSID: ~ 55 | WiFi pre-shared Key: ~ 56 | 'Brightness: 60%': ~ 57 | "Dark\nLight": ~ 58 | 'Timeout: 60s': ~ 59 | Screen Lock: ~ 60 | Settings Lock: ~ 61 | Lock PIN: ~ 62 | Mouse: ~ 63 | none: ~ 64 | Keyboard: ~ 65 | Message Alert: ~ 66 | Ringtone: ~ 67 | Default: ~ 68 | Zone: ~ 69 | City: ~ 70 | Backup: ~ 71 | Restore: ~ 72 | Public/Private Key: ~ 73 | "NodeDB Reset\nFactory Reset\nClear Chat History": ~ 74 | Channel Name: ~ 75 | Pre-shared Key: ~ 76 | Filter: ~ 77 | Unknown: ~ 78 | Offline: ~ 79 | Public Key: ~ 80 | Channel: ~ 81 | Hops away: ~ 82 | MQTT: ~ 83 | Position: ~ 84 | Name: ~ 85 | Highlight: ~ 86 | Active Chat: ~ 87 | Telemetry: ~ 88 | IAQ: ~ 89 | Start: ~ 90 | "choose\nnode": ~ 91 | choose target node: ~ 92 | "New Message from\n": ~ 93 | Restoring messages ...: ~ 94 | Please set region and name: ~ 95 | Region: ~ 96 | Resync ...: ~ 97 | OK: ~ 98 | Cancel: ~ 99 | now: ~ 100 | 'Lock: %s/%s': ~ 101 | 'on': ~ 102 | 'off': ~ 103 | 'Screen Calibration: %s': ~ 104 | done: ~ 105 | default: ~ 106 | "Client\nClient Mute\nTracker\nSensor\nTAK\nClient Hidden\nLost & Found\nTAK Tracker": ~ 107 | Rebooting ...: ~ 108 | '>> Programming mode <<': ~ 109 | Enter Text ...: ~ 110 | '!Enter Filter ...': ~ 111 | Enter Filter ...: ~ 112 | 'FrequencySlot: %d (%g MHz)': ~ 113 | 'Brightness: %d%%': ~ 114 | 'Timeout: off': ~ 115 | 'Timeout: %ds': ~ 116 | No map tiles found on SDCard!: Map tiles not found! 117 | Locations Map (%d/%d): ~ 118 | Stop: ~ 119 | 'heard: !%08x': ~ 120 | 'Packet Log: %d': ~ 121 | 'Language: %s': ~ 122 | 'Screen Timeout: off': ~ 123 | 'Screen Timeout: %ds': ~ 124 | 'Screen Brightness: %d%%': ~ 125 | 'Theme: %s': ~ 126 | 'Region: %s': ~ 127 | 'User name: %s': ~ 128 | 'Device Role: %s': ~ 129 | 'Modem Preset: %s': ~ 130 | 'WiFi: %s': ~ 131 | : ~ 132 | Util %0.1f%% Air %0.1f%%: ~ 133 | 'hops: %d': ~ 134 | unknown: ~ 135 | Shutting down ...: ~ 136 | region unset: ~ 137 | Banner & Sound: ~ 138 | Banner only: ~ 139 | Sound only: ~ 140 | 'Message Alert: %s': ~ 141 | 'Channel: %s': ~ 142 | Failed to write keys!: ~ 143 | Failed to restore keys!: ~ 144 | Failed to parse keys!: ~ 145 | Failed to retrieve keys!: ~ 146 | '%d active chat(s)': 147 | one: ~ 148 | other: ~ 149 | "New message from \n%s": ~ 150 | 'Input Control: %s/%s': ~ 151 | '%d of %d nodes online': 152 | one: ~ 153 | other: ~ 154 | 'Filter: %d of %d nodes': ~ 155 | '%d new message': ~ 156 | '%d new messages': ~ 157 | 'uptime: %02d:%02d:%02d': ~ 158 | "%s (%0.1f GB)\nUsed: %d MB (%d%%)": ~ 159 | "Heap: %d (%d%%)\nLVGL: %d (%d%%)": ~ 160 | -------------------------------------------------------------------------------- /locale/lv_i18n.h: -------------------------------------------------------------------------------- 1 | #ifndef LV_I18N_H 2 | #define LV_I18N_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | typedef enum { 12 | LV_I18N_PLURAL_TYPE_ZERO, 13 | LV_I18N_PLURAL_TYPE_ONE, 14 | LV_I18N_PLURAL_TYPE_TWO, 15 | LV_I18N_PLURAL_TYPE_FEW, 16 | LV_I18N_PLURAL_TYPE_MANY, 17 | LV_I18N_PLURAL_TYPE_OTHER, 18 | _LV_I18N_PLURAL_TYPE_NUM, 19 | } lv_i18n_plural_type_t; 20 | 21 | typedef struct { 22 | const char *msg_id; 23 | const char *translation; 24 | } lv_i18n_phrase_t; 25 | 26 | typedef struct { 27 | const char *locale_name; 28 | lv_i18n_phrase_t *singulars; 29 | lv_i18n_phrase_t *plurals[_LV_I18N_PLURAL_TYPE_NUM]; 30 | uint8_t (*locale_plural_fn)(int32_t num); 31 | } lv_i18n_lang_t; 32 | 33 | // Null-terminated list of languages. First one used as default. 34 | typedef const lv_i18n_lang_t *lv_i18n_language_pack_t; 35 | 36 | extern const lv_i18n_language_pack_t lv_i18n_language_pack[]; 37 | 38 | /** 39 | * Set the languages for internationalization 40 | * @param langs pointer to the array of languages. (Last element has to be `NULL`) 41 | */ 42 | int lv_i18n_init(const lv_i18n_language_pack_t *langs); 43 | 44 | /** 45 | * Change the localization (language) 46 | * @param l_name name of the translation locale to use. E.g. "en_GB" 47 | */ 48 | int lv_i18n_set_locale(const char *l_name); 49 | 50 | /** 51 | * Get the translation from a message ID 52 | * @param msg_id message ID 53 | * @return the translation of `msg_id` on the set local 54 | */ 55 | const char *lv_i18n_get_text(const char *msg_id); 56 | 57 | /** 58 | * Get the translation from a message ID and apply the language's plural rule to get correct form 59 | * @param msg_id message ID 60 | * @param num an integer to select the correct plural form 61 | * @return the translation of `msg_id` on the set local 62 | */ 63 | const char *lv_i18n_get_text_plural(const char *msg_id, int32_t num); 64 | 65 | /** 66 | * Get the name of the currently used localization. 67 | * @return name of the currently used localization. E.g. "en_GB" 68 | */ 69 | const char *lv_i18n_get_current_locale(void); 70 | 71 | void __lv_i18n_reset(void); 72 | 73 | #define _(text) lv_i18n_get_text(text) 74 | #define _p(text, num) lv_i18n_get_text_plural(text, num) 75 | 76 | #ifdef __cplusplus 77 | } /* extern "C" */ 78 | #endif 79 | 80 | #endif /*LV_LANG_H*/ 81 | -------------------------------------------------------------------------------- /locale/ro.yml: -------------------------------------------------------------------------------- 1 | ro: 2 | no new messages: ~ 3 | 1 of 1 nodes online: ~ 4 | 'uptime 00:00:00': ~ 5 | "Heap: 0\nLVGL: 0": ~ 6 | DEL: ~ 7 | 'User name: ': ~ 8 | 'Modem Preset: LONG FAST': ~ 9 | 'Channel: LongFast': ~ 10 | 'Role: Client': ~ 11 | 'Screen Timeout: 60s': ~ 12 | 'Screen Lock: off': ~ 13 | 'Screen Brightness: 60%': ~ 14 | 'Theme: Dark': ~ 15 | 'Screen Calibration: default': ~ 16 | 'Input Control: none/none': ~ 17 | 'Message Alert Buzzer: on': ~ 18 | 'Language: English': ~ 19 | Configuration Reset: ~ 20 | Reboot / Shutdown: ~ 21 | Mesh Detector: ~ 22 | Signal Scanner: ~ 23 | Trace Route: ~ 24 | Neighbors: ~ 25 | Statistics: ~ 26 | Packet Log: ~ 27 | Meshtastic: ~ 28 | Group Channels: ~ 29 | no messages: ~ 30 | Settings & Tools: ~ 31 | Settings (advanced): ~ 32 | Locations Map: ~ 33 | 'Locations Map (%d/%d)': ~ 34 | no chats: ~ 35 | Node Search: ~ 36 | Packet Statistics: ~ 37 | Node Options: ~ 38 | Short Name: ~ 39 | Long Name: ~ 40 | : ~ 41 | "LONG FAST\nLONG SLOW\n-- deprecated --\nMEDIUM SLOW\nMEDIUM FAST\nSHORT SLOW\nSHORT FAST\nLONG MODERATE\nSHORT TURBO": ~ 42 | "Client\nClient Mute\nRouter\n-- deprecated --\nRepeater\nTracker\nSensor\nTAK\nClient Hidden\nLost & Found\nTAK Tracker": ~ 43 | 'Brightness: 60%': ~ 44 | "Dark\nLight": ~ 45 | 'Timeout: 60s': ~ 46 | Screen Lock: ~ 47 | Lock PIN: ~ 48 | Mouse: ~ 49 | none: ~ 50 | Keyboard: ~ 51 | Message Alert: ~ 52 | Ringtone: ~ 53 | Default: ~ 54 | Zone: ~ 55 | City: ~ 56 | "NodeDB Reset\nFactory Reset": ~ 57 | Channel Name: ~ 58 | Pre-shared Key: ~ 59 | Unknown: ~ 60 | Offline: ~ 61 | Public Key: ~ 62 | Hops away: ~ 63 | MQTT: ~ 64 | Position: ~ 65 | Name: ~ 66 | Active Chat: ~ 67 | Telemetry: ~ 68 | IAQ: ~ 69 | Start: ~ 70 | "New Message from\n": ~ 71 | Resync...: ~ 72 | OK: ~ 73 | Cancel: ~ 74 | 'FrequencySlot: %d (%g MHz)': ~ 75 | 'Brightness: %d%%': ~ 76 | 'Timeout: off': ~ 77 | 'Timeout: %ds': ~ 78 | 'Screen Calibration: %s': ~ 79 | done: ~ 80 | default: ~ 81 | Stop: ~ 82 | "choose\nnode": ~ 83 | choose target node: ~ 84 | 'heard: !%08x': ~ 85 | 'Packet Log: %d': ~ 86 | 'Language: %s': ~ 87 | 'Screen Timeout: off': ~ 88 | 'Screen Timeout: %ds': ~ 89 | 'Screen Brightness: %d%%': ~ 90 | 'Theme: %s': ~ 91 | 'User name: %s': ~ 92 | 'Device Role: %s': ~ 93 | 'Region: %s': ~ 94 | 'Modem Preset: %s': ~ 95 | 'Channel: %s': ~ 96 | 'Screen Lock: %s': ~ 97 | 'on': ~ 98 | 'off': ~ 99 | 'Message Alert: %s': ~ 100 | Util %0.1f%% Air %0.1f%%: ~ 101 | 'hops: %d': ~ 102 | unknown: ~ 103 | : ~ 104 | '%d active chat(s)': ~ 105 | "New message from \n%s": ~ 106 | 'Input Control: %s/%s': ~ 107 | '%d of %d nodes online': ~ 108 | Filtering ...: ~ 109 | 'Filter: %d of %d nodes': ~ 110 | now: ~ 111 | '%d new message': ~ 112 | '%d new messages': ~ 113 | 'uptime: %02d:%02d:%02d': ~ 114 | "Heap: %d (%d%%)\nLVGL: %d (%d%%)": ~ 115 | Settings: ~ 116 | Tools: ~ 117 | Filter: ~ 118 | Highlight: ~ 119 | Primary Channel: ~ 120 | Secondary Channels: ~ 121 | Resync ...: ~ 122 | Rebooting ...: ~ 123 | Shutting down ...: ~ 124 | LoRa 0.0 MHz: ~ 125 | silent: ~ 126 | 'WiFi: ': ~ 127 | 'Lock: off/off': ~ 128 | LoRa TX off!: ~ 129 | WiFi SSID: ~ 130 | WiFi pre-shared Key: ~ 131 | Settings Lock: ~ 132 | 'Lock: %s/%s': ~ 133 | Enter Text ...: ~ 134 | '!Enter Filter ...': ~ 135 | Enter Filter ...: ~ 136 | 'WiFi: %s': ~ 137 | : ~ 138 | region unset: ~ 139 | Banner & Sound: ~ 140 | Banner only: ~ 141 | Sound only: ~ 142 | no signal: ~ 143 | Restoring messages ...: ~ 144 | -------------------------------------------------------------------------------- /maps/atlas.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/maps/atlas.zip -------------------------------------------------------------------------------- /maps/dark-matter-brown.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/maps/dark-matter-brown.zip -------------------------------------------------------------------------------- /maps/osm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/maps/osm.zip -------------------------------------------------------------------------------- /maps/positron.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meshtastic/device-ui/1b520fcb168c7447a8d6a6ebc56954c9f472e964/maps/positron.zip -------------------------------------------------------------------------------- /portduino/Light_PWM.cpp: -------------------------------------------------------------------------------- 1 | #include "Light_PWM.h" 2 | 3 | namespace portduino 4 | { 5 | 6 | /** 7 | * Configure PWM, fallback to BL on/off if no pwm available 8 | */ 9 | bool Light_PWM::init(uint8_t brightness) 10 | { 11 | if (_cfg.pin_bl >= 0) { 12 | if (_cfg.pwm_channel == -1) { 13 | setBrightness(brightness); 14 | } else { 15 | // ledcAttachChannel(_cfg.pin_bl, _cfg.freq, _cfg.pwm_channel); 16 | } 17 | } 18 | return true; 19 | } 20 | 21 | void Light_PWM::setBrightness(uint8_t brightness) 22 | { 23 | if (_cfg.pwm_channel == -1) { 24 | if (brightness == 0) { 25 | digitalWrite(_cfg.pin_bl, _cfg.invert); 26 | } else { 27 | digitalWrite(_cfg.pin_bl, !_cfg.invert); 28 | } 29 | } else { 30 | uint32_t duty_cycle = _cfg.freq * 1000 * brightness / 255; 31 | // ledcWrite(_cfg.pin_bl, duty_cycle); 32 | } 33 | } 34 | 35 | } // namespace portduino -------------------------------------------------------------------------------- /portduino/Light_PWM.h: -------------------------------------------------------------------------------- 1 | #include "LovyanGFX.hpp" 2 | 3 | namespace portduino 4 | { 5 | 6 | class Light_PWM : public lgfx::v1::ILight 7 | { 8 | public: 9 | struct config_t { 10 | uint32_t freq = 1200; 11 | int16_t pin_bl = -1; 12 | uint8_t offset = 0; 13 | int8_t pwm_channel = -1; 14 | 15 | bool invert = false; 16 | }; 17 | 18 | const config_t &config(void) const { return _cfg; } 19 | void config(const config_t &cfg) { _cfg = cfg; } 20 | 21 | bool init(uint8_t brightness) override; 22 | void setBrightness(uint8_t brightness) override; 23 | 24 | private: 25 | config_t _cfg; 26 | }; 27 | 28 | } // namespace portduino 29 | -------------------------------------------------------------------------------- /source/comms/packet/PacketClient.cpp: -------------------------------------------------------------------------------- 1 | #include "comms/PacketClient.h" 2 | #include "util/ILog.h" 3 | #include "util/Packet.h" 4 | #include "util/SharedQueue.h" 5 | #include 6 | 7 | const uint32_t max_packet_queue_size = 25; 8 | 9 | void PacketClient::init(void) 10 | { 11 | // sharedQueue is currently defined external, it is not shared between processes 12 | connect(sharedQueue); 13 | } 14 | 15 | PacketClient::PacketClient() : queue(nullptr) {} 16 | 17 | bool PacketClient::connect(void) 18 | { 19 | is_connected = true; 20 | return is_connected; 21 | } 22 | 23 | bool PacketClient::disconnect(void) 24 | { 25 | is_connected = false; 26 | return is_connected; 27 | } 28 | 29 | bool PacketClient::isConnected(void) 30 | { 31 | return is_connected; 32 | } 33 | 34 | bool PacketClient::isStandalone(void) 35 | { 36 | return false; 37 | } 38 | 39 | int PacketClient::connect(SharedQueue *_queue) 40 | { 41 | if (!queue) { 42 | queue = _queue; 43 | } else if (_queue != queue) { 44 | ILOG_WARN("Client already connected."); 45 | } 46 | is_connected = true; 47 | return queue->serverQueueSize(); 48 | } 49 | 50 | bool PacketClient::send(meshtastic_ToRadio &&to) 51 | { 52 | if (available()) { 53 | static uint32_t id = 0; 54 | return queue->clientSend(DataPacket(++id, to)); 55 | } else { 56 | ILOG_WARN("Cannot send DataPacket"); 57 | return false; 58 | } 59 | } 60 | 61 | meshtastic_FromRadio PacketClient::receive(void) 62 | { 63 | if (hasData()) { 64 | auto p = queue->clientReceive(); 65 | if (p) { 66 | return static_cast *>(p->move().get())->getData(); 67 | } 68 | } 69 | return meshtastic_FromRadio(); 70 | } 71 | 72 | bool PacketClient::hasData() const 73 | { 74 | assert(queue); 75 | return queue->serverQueueSize() > 0; 76 | } 77 | 78 | bool PacketClient::available() const 79 | { 80 | assert(queue); 81 | return queue->clientQueueSize() < max_packet_queue_size; 82 | } 83 | -------------------------------------------------------------------------------- /source/comms/packet/PacketServer.cpp: -------------------------------------------------------------------------------- 1 | #include "comms/PacketServer.h" 2 | #include "util/SharedQueue.h" 3 | #include 4 | 5 | const uint32_t max_packet_queue_size = 300; 6 | 7 | SharedQueue *sharedQueue = nullptr; 8 | 9 | PacketServer *packetServer = nullptr; 10 | 11 | PacketServer::PacketServer() : queue(nullptr) {} 12 | 13 | PacketServer *PacketServer::init(void) 14 | { 15 | packetServer = new PacketServer; 16 | sharedQueue = new SharedQueue; 17 | packetServer->begin(sharedQueue); 18 | return packetServer; 19 | } 20 | 21 | void PacketServer::begin(SharedQueue *_queue) 22 | { 23 | queue = _queue; 24 | } 25 | 26 | Packet::PacketPtr PacketServer::receivePacket(void) 27 | { 28 | assert(queue); 29 | if (queue->clientQueueSize() == 0) 30 | return {nullptr}; 31 | return queue->serverReceive(); 32 | } 33 | 34 | bool PacketServer::sendPacket(Packet &&p) 35 | { 36 | assert(queue); 37 | if (queue->serverQueueSize() >= max_packet_queue_size) { 38 | return false; 39 | } 40 | queue->serverSend(std::move(p)); 41 | return true; 42 | } 43 | 44 | bool PacketServer::hasData() const 45 | { 46 | assert(queue); 47 | return queue->clientQueueSize() > 0; 48 | } 49 | 50 | bool PacketServer::available() const 51 | { 52 | assert(queue); 53 | return queue->serverQueueSize() < max_packet_queue_size; 54 | } 55 | -------------------------------------------------------------------------------- /source/comms/serial/BluetoothClient.cpp: -------------------------------------------------------------------------------- 1 | #include "comms/BluetoothClient.h" 2 | -------------------------------------------------------------------------------- /source/comms/serial/WLANClient.cpp: -------------------------------------------------------------------------------- 1 | #include "comms/WLANClient.h" 2 | -------------------------------------------------------------------------------- /source/graphics/DeviceGUI.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/DeviceGUI.h" 2 | #include "graphics/driver/DisplayDriver.h" 3 | #include "graphics/driver/DisplayDriverConfig.h" 4 | #include "input/InputDriver.h" 5 | #include 6 | 7 | #if LV_USE_LIBINPUT 8 | #include "input/LinuxInputDriver.h" 9 | static LinuxInputDriver *linuxInputDriver = nullptr; 10 | #else 11 | #if defined(INPUTDRIVER_I2C_KBD_TYPE) 12 | #include "input/I2CKeyboardInputDriver.h" 13 | static I2CKeyboardInputDriver *keyboardDriver = nullptr; 14 | #endif 15 | #if defined(INPUTDRIVER_ENCODER_TYPE) 16 | #include "input/EncoderInputDriver.h" 17 | static EncoderInputDriver *encoderDriver = nullptr; 18 | #endif 19 | #if defined(INPUTDRIVER_MATRIX_TYPE) 20 | #include "input/KeyMatrixInputDriver.h" 21 | static KeyMatrixInputDriver *keyMatrixDriver = nullptr; 22 | #endif 23 | #if defined(INPUTDRIVER_BUTTON_TYPE) 24 | #include "input/ButtonInputDriver.h" 25 | static ButtonInputDriver *buttonDriver = nullptr; 26 | #endif 27 | #endif 28 | #include "lvgl.h" 29 | #include "ui.h" 30 | #include "util/ILog.h" 31 | 32 | DeviceGUI::DeviceGUI(const DisplayDriverConfig *cfg, DisplayDriver *driver) : displaydriver(driver), inputdriver(nullptr) 33 | { 34 | #if LV_USE_LIBINPUT 35 | if (cfg) 36 | linuxInputDriver = new LinuxInputDriver(cfg->keyboard(), cfg->pointer()); 37 | // else 38 | // linuxInputDriver = InputDriver::instance(); 39 | #else 40 | #if defined(INPUTDRIVER_I2C_KBD_TYPE) 41 | keyboardDriver = new I2CKeyboardInputDriver; 42 | #endif 43 | #if defined(INPUTDRIVER_ENCODER_TYPE) 44 | encoderDriver = new EncoderInputDriver; 45 | #endif 46 | #if defined(INPUTDRIVER_MATRIX_TYPE) 47 | keyMatrixDriver = new KeyMatrixInputDriver; 48 | #endif 49 | #if defined(INPUTDRIVER_BUTTON_TYPE) 50 | buttonDriver = new ButtonInputDriver; 51 | #endif 52 | #endif 53 | if (!inputdriver) 54 | inputdriver = InputDriver::instance(); 55 | } 56 | 57 | void DeviceGUI::init(IClientBase *client) 58 | { 59 | ILOG_DEBUG("Display driver init..."); 60 | displaydriver->init(this); 61 | 62 | ILOG_DEBUG("Input driver init..."); 63 | #if LV_USE_LIBINPUT 64 | if (linuxInputDriver) 65 | linuxInputDriver->init(); 66 | #endif 67 | #if defined(INPUTDRIVER_I2C_KBD_TYPE) 68 | if (keyboardDriver) 69 | keyboardDriver->init(); 70 | #endif 71 | #if defined(INPUTDRIVER_ENCODER_TYPE) 72 | if (encoderDriver) 73 | encoderDriver->init(); 74 | #endif 75 | #if defined(INPUTDRIVER_MATRIX_TYPE) 76 | if (keyMatrixDriver) 77 | keyMatrixDriver->init(); 78 | #endif 79 | #if defined(INPUTDRIVER_BUTTON_TYPE) 80 | if (buttonDriver) 81 | buttonDriver->init(); 82 | #endif 83 | if (inputdriver) 84 | inputdriver->init(); 85 | 86 | displaydriver->printConfig(); 87 | } 88 | 89 | /** 90 | * Linux: measure how long it takes to call displaydriver->task_handler(). 91 | * Then tell the lvgl library how long it took via lv_tick_inc(). 92 | */ 93 | void DeviceGUI::task_handler(void) 94 | { 95 | #if defined(ARCH_PORTDUINO) 96 | int ms = 10; 97 | auto start = std::chrono::high_resolution_clock::now(); 98 | displaydriver->task_handler(); 99 | auto stop = std::chrono::high_resolution_clock::now(); 100 | auto duration = std::chrono::duration_cast(stop - start); 101 | if (duration.count() < ms) { 102 | std::this_thread::sleep_for(std::chrono::milliseconds(ms - duration.count())); 103 | lv_tick_inc(ms); 104 | } else { 105 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 106 | lv_tick_inc(duration.count() + 1); 107 | } 108 | #else 109 | displaydriver->task_handler(); 110 | #endif 111 | }; 112 | 113 | DeviceGUI::~DeviceGUI() 114 | { 115 | delete inputdriver; 116 | } -------------------------------------------------------------------------------- /source/graphics/DeviceScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/DeviceScreen.h" 2 | #include "Arduino.h" 3 | #include "graphics/common/ViewFactory.h" 4 | #include "util/ILog.h" 5 | 6 | #if defined(ARDUINO_ARCH_ESP32) 7 | #include "freertos/FreeRTOS.h" 8 | #include "freertos/semphr.h" 9 | 10 | static SemaphoreHandle_t xSemaphore = nullptr; 11 | #endif 12 | 13 | DeviceScreen &DeviceScreen::create(void) 14 | { 15 | return *new DeviceScreen(nullptr); 16 | } 17 | 18 | DeviceScreen &DeviceScreen::create(const DisplayDriverConfig *cfg) 19 | { 20 | return *new DeviceScreen(cfg); 21 | } 22 | 23 | DeviceScreen &DeviceScreen::create(DisplayDriverConfig &&cfg) 24 | { 25 | return *new DeviceScreen(std::move(cfg)); 26 | } 27 | 28 | DeviceScreen::DeviceScreen(const DisplayDriverConfig *cfg) 29 | { 30 | if (cfg) { 31 | gui = ViewFactory::create(*cfg); 32 | } else { 33 | gui = ViewFactory::create(); 34 | } 35 | #if defined(ARDUINO_ARCH_ESP32) 36 | xSemaphore = xSemaphoreCreateMutex(); 37 | if (!xSemaphore) 38 | ILOG_ERROR("DeviceScreen: xSemaphoreCreateMutex() failed"); 39 | #endif 40 | } 41 | 42 | DeviceScreen::DeviceScreen(DisplayDriverConfig &&cfg) 43 | { 44 | gui = ViewFactory::create(cfg); 45 | } 46 | 47 | void DeviceScreen::init(IClientBase *client) 48 | { 49 | ILOG_DEBUG("DeviceScreen::init()..."); 50 | gui->init(client); 51 | 52 | // #ifdef TFT_BL 53 | // digitalWrite(TFT_BL, HIGH); 54 | // pinMode(TFT_BL, OUTPUT); 55 | // #endif 56 | 57 | #ifdef VTFT_CTRL 58 | digitalWrite(VTFT_CTRL, LOW); 59 | pinMode(VTFT_CTRL, OUTPUT); 60 | #endif 61 | ILOG_DEBUG("DeviceScreen::init() done."); 62 | } 63 | 64 | void DeviceScreen::task_handler(void) 65 | { 66 | gui->task_handler(); 67 | } 68 | 69 | #if defined(ARDUINO_ARCH_ESP32) 70 | int DeviceScreen::prepareSleep(void *) 71 | { 72 | if (xSemaphore) 73 | return xSemaphoreTake(xSemaphore, pdMS_TO_TICKS(1000)) == pdTRUE ? 0 : 1; 74 | else 75 | return 1; 76 | } 77 | 78 | int DeviceScreen::wakeUp(esp_sleep_wakeup_cause_t cause) 79 | { 80 | if (xSemaphore) 81 | return xSemaphoreGive(xSemaphore) == pdTRUE ? 0 : 1; 82 | else 83 | return 1; 84 | } 85 | #endif 86 | 87 | /** 88 | * @brief synchronisation point: here we sleep after prepareSleep() was called 89 | */ 90 | void DeviceScreen::sleep(uint32_t sleepTime) 91 | { 92 | #if defined(ARCH_ESP32) 93 | if (xSemaphore && xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) 94 | xSemaphoreGive(xSemaphore); 95 | vTaskDelay((TickType_t)sleepTime); // yield, do not remove 96 | #endif 97 | } 98 | -------------------------------------------------------------------------------- /source/graphics/LVGL/LVGLGraphics.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/LVGL/LVGLGraphics.h" 2 | #include "assert.h" 3 | #include "util/ILog.h" 4 | 5 | LVGLGraphics::LVGLGraphics(uint16_t width, uint16_t height) : screenWidth(width), screenHeight(height) {} 6 | 7 | void LVGLGraphics::init(void) 8 | { 9 | ILOG_DEBUG("LV init..."); 10 | #if LV_USE_LOG 11 | lv_log_register_print_cb(lv_debug); 12 | #endif 13 | lv_init(); 14 | #if LV_USE_LOG 15 | lv_log_register_print_cb(lv_debug); 16 | #endif 17 | } 18 | 19 | // debugging callback 20 | void LVGLGraphics::lv_debug(lv_log_level_t level, const char *buf) 21 | { 22 | switch (level) { 23 | case LV_LOG_LEVEL_TRACE: { 24 | ILOG_DEBUG("%s", buf); 25 | break; 26 | } 27 | case LV_LOG_LEVEL_INFO: { 28 | ILOG_INFO("%s", buf); 29 | break; 30 | } 31 | case LV_LOG_LEVEL_WARN: { 32 | ILOG_WARN("%s", buf); 33 | break; 34 | } 35 | case LV_LOG_LEVEL_ERROR: { 36 | ILOG_ERROR("%s", buf); 37 | break; 38 | } 39 | default: 40 | ILOG_DEBUG("%s", buf); 41 | break; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/graphics/OLED/OLEDViewController.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/view/OLED/OLEDViewController.h" 2 | 3 | OLEDViewController::OLEDViewController() {} 4 | 5 | void OLEDViewController::init(MeshtasticView *gui, IClientBase *_client) 6 | { 7 | ViewController::init(gui, _client); 8 | } 9 | -------------------------------------------------------------------------------- /source/graphics/OLED/OLEDView_128x64.cpp: -------------------------------------------------------------------------------- 1 | #if HAS_TFT // VIEW_128x64 2 | 3 | #include "graphics/view/OLED/OLEDView_128x64.h" 4 | #include "graphics/driver/DisplayDriverFactory.h" 5 | #include "graphics/view/OLED/OLEDViewController.h" 6 | #include "ui.h" // this is the ui generated by eez-studio 7 | #include "util/ILog.h" 8 | #include 9 | #include 10 | #include 11 | 12 | OLEDView_128x64 *OLEDView_128x64::gui = nullptr; 13 | 14 | OLEDView_128x64 *OLEDView_128x64::instance(void) 15 | { 16 | if (!gui) 17 | gui = new OLEDView_128x64(nullptr, DisplayDriverFactory::create(128, 64)); 18 | return gui; 19 | } 20 | 21 | OLEDView_128x64 *OLEDView_128x64::instance(const DisplayDriverConfig &cfg) 22 | { 23 | if (!gui) 24 | gui = new OLEDView_128x64(&cfg, DisplayDriverFactory::create(cfg)); 25 | return gui; 26 | } 27 | 28 | OLEDView_128x64::OLEDView_128x64(const DisplayDriverConfig *cfg, DisplayDriver *driver) 29 | : MeshtasticView(cfg, driver, new OLEDViewController) 30 | { 31 | } 32 | 33 | void OLEDView_128x64::init(IClientBase *client) 34 | { 35 | ILOG_DEBUG("OLEDView_128x64 init..."); 36 | MeshtasticView::init(client); 37 | // ui_events_init(); 38 | } 39 | 40 | void OLEDView_128x64::task_handler(void) 41 | { 42 | MeshtasticView::task_handler(); 43 | } 44 | #endif -------------------------------------------------------------------------------- /source/graphics/TFT/TFTView_160x80.cpp: -------------------------------------------------------------------------------- 1 | #if HAS_TFT // VIEW_160x80 2 | 3 | #include "graphics/view/TFT/TFTView_160x80.h" 4 | #include "graphics/common/ViewController.h" 5 | #include "graphics/driver/DisplayDriverFactory.h" 6 | #include "ui.h" // this is the ui generated by lvgl / squareline editor 7 | #include "util/ILog.h" 8 | #include 9 | #include 10 | #include 11 | 12 | TFTView_160x80 *TFTView_160x80::gui = nullptr; 13 | 14 | TFTView_160x80 *TFTView_160x80::instance(void) 15 | { 16 | if (!gui) 17 | gui = new TFTView_160x80(nullptr, DisplayDriverFactory::create(160, 80)); 18 | return gui; 19 | } 20 | 21 | TFTView_160x80 *TFTView_160x80::instance(const DisplayDriverConfig &cfg) 22 | { 23 | if (!gui) 24 | gui = new TFTView_160x80(&cfg, DisplayDriverFactory::create(cfg)); 25 | return gui; 26 | } 27 | 28 | TFTView_160x80::TFTView_160x80(const DisplayDriverConfig *cfg, DisplayDriver *driver) 29 | : MeshtasticView(cfg, driver, new ViewController) 30 | { 31 | } 32 | 33 | void TFTView_160x80::init(IClientBase *client) 34 | { 35 | ILOG_DEBUG("TFTView_160x80 init..."); 36 | MeshtasticView::init(client); 37 | } 38 | 39 | void TFTView_160x80::task_handler(void) 40 | { 41 | MeshtasticView::task_handler(); 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /source/graphics/TFT/TFTView_240x240.cpp: -------------------------------------------------------------------------------- 1 | #if HAS_TFT // VIEW_240x240 2 | 3 | #include "graphics/view/TFT/TFTView_240x240.h" 4 | #include "graphics/common/ViewController.h" 5 | #include "graphics/driver/DisplayDriverFactory.h" 6 | #include "ui.h" // this is the ui generated by lvgl / squareline editor 7 | #include "util/ILog.h" 8 | #include 9 | #include 10 | 11 | TFTView_240x240 *TFTView_240x240::gui = nullptr; 12 | 13 | TFTView_240x240 *TFTView_240x240::instance(void) 14 | { 15 | if (!gui) 16 | gui = new TFTView_240x240(nullptr, DisplayDriverFactory::create(240, 240)); 17 | return gui; 18 | } 19 | 20 | TFTView_240x240 *TFTView_240x240::instance(const DisplayDriverConfig &cfg) 21 | { 22 | if (!gui) 23 | gui = new TFTView_240x240(&cfg, DisplayDriverFactory::create(cfg)); 24 | return gui; 25 | } 26 | 27 | TFTView_240x240::TFTView_240x240(const DisplayDriverConfig *cfg, DisplayDriver *driver) 28 | : MeshtasticView(cfg, driver, new ViewController) 29 | { 30 | } 31 | 32 | void TFTView_240x240::init(IClientBase *client) 33 | { 34 | ILOG_DEBUG("TFTView init..."); 35 | MeshtasticView::init(client); 36 | 37 | // ui_set_active(ui_HomeButton, ui_HomePanel, ui_TopPanel); 38 | // ui_events_init(); 39 | 40 | // keyboard init 41 | // lv_keyboard_set_textarea(ui_Keyboard, ui_MessageInputArea); 42 | } 43 | 44 | void TFTView_240x240::task_handler(void) 45 | { 46 | MeshtasticView::task_handler(); 47 | } 48 | 49 | #endif -------------------------------------------------------------------------------- /source/graphics/TFT/TFTView_480x320.cpp: -------------------------------------------------------------------------------- 1 | #if HAS_TFT // VIEW_480x320 2 | 3 | #include "graphics/view/TFT/TFTView_480x320.h" 4 | #include "graphics/common/ViewController.h" 5 | #include "graphics/driver/DisplayDriverFactory.h" 6 | #include "ui.h" // this is the ui generated by lvgl / squareline editor 7 | #include "util/ILog.h" 8 | #include 9 | #include 10 | #include 11 | 12 | TFTView_480x320 *TFTView_480x320::gui = nullptr; 13 | 14 | TFTView_480x320 *TFTView_480x320::instance(void) 15 | { 16 | if (!gui) 17 | gui = new TFTView_480x320(nullptr, DisplayDriverFactory::create(480, 320)); 18 | return gui; 19 | } 20 | 21 | TFTView_480x320 *TFTView_480x320::instance(const DisplayDriverConfig &cfg) 22 | { 23 | if (!gui) 24 | gui = new TFTView_480x320(&cfg, DisplayDriverFactory::create(cfg)); 25 | return gui; 26 | } 27 | 28 | TFTView_480x320::TFTView_480x320(const DisplayDriverConfig *cfg, DisplayDriver *driver) 29 | : MeshtasticView(cfg, driver, new ViewController) 30 | { 31 | } 32 | 33 | void TFTView_480x320::init(IClientBase *client) 34 | { 35 | ILOG_DEBUG("TFTView_480x320 init..."); 36 | MeshtasticView::init(client); 37 | } 38 | 39 | void TFTView_480x320::task_handler(void) 40 | { 41 | MeshtasticView::task_handler(); 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /source/graphics/common/BatteryLevel.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/common/BatteryLevel.h" 2 | 3 | BatteryLevel::BatteryLevel(void) 4 | : levels{ 5 | {100, 0.0f}, // Plugged 6 | {100, (float)CHARGING_VOLTAGE}, // Charging 7 | {80, 4.00f}, // Full 8 | {35, 3.50f}, // Mid 9 | {10, 3.30f}, // Low 10 | {0, 3.12f}, // Empty 11 | {0, 3.10f} // Warn 12 | } 13 | { 14 | } 15 | 16 | BatteryLevel::Status BatteryLevel::calcStatus(uint32_t percentage, float voltage) 17 | { 18 | if (voltage == levels[Plugged].voltage) { 19 | return Plugged; 20 | } 21 | if (percentage >= levels[Charging].percentage && voltage > levels[Charging].voltage) { 22 | return Charging; 23 | } else if (percentage >= levels[Full].percentage && voltage > levels[Full].voltage) { 24 | return Full; 25 | } else if (percentage >= levels[Mid].percentage && voltage > levels[Mid].voltage) { 26 | return Mid; 27 | } else if (percentage >= levels[Low].percentage && voltage > levels[Low].voltage) { 28 | return Low; 29 | } else if (percentage > levels[Empty].percentage) { 30 | return Empty; 31 | } else { 32 | return Warn; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/graphics/common/LoRaPresets.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/common/LoRaPresets.h" 2 | 3 | /** 4 | * LoRa Presets 5 | * Note: keep order the same as in meshtastic_Config_LoRaConfig_RegionCode and as in settings_region_dropdown 6 | */ 7 | LoRaPresets::RegionInfo LoRaPresets::regionInfo[] = { 8 | {"UNSET", 902.0f, 928.0f}, {"US", 902.0f, 928.0f}, {"EU_433", 433.0f, 434.0f}, {"EU_868", 869.4f, 869.65f}, 9 | {"CN", 470.0f, 510.0f}, {"JP", 920.8f, 927.8f}, {"ANZ", 915.0f, 928.0f}, {"KR", 920.0f, 923.0f}, 10 | {"TW", 920.0f, 925.0f}, {"RU", 868.7f, 869.2f}, {"IN", 865.0f, 867.0f}, {"NZ_865", 864.0f, 868.0f}, 11 | {"TH", 920.0f, 925.0f}, {"LORA_24", 2400.0f, 2483.5f}, {"UA_433", 433.0f, 434.7f}, {"UA_868", 868.0f, 868.6f}, 12 | {"MY_433", 433.0f, 435.0f}, {"MY_919", 919.0f, 924.0f}, {"SG_923", 917.0f, 925.0f}, {"PH_433", 433.0f, 434.7f}, 13 | {"PH_868", 868.0f, 869.4f}, {"PH_915", 915.0f, 918.0f}}; 14 | 15 | LoRaPresets::ModemPreset LoRaPresets::modemPreset[] = { 16 | {"LongFast", "250", .250f}, {"LongSlow", "125", .125f}, {"VLongSlow", "62.5", .0625f}, 17 | {"MediumSlow", "250", .250f}, {"MediumFast", "250", .250f}, {"ShortSlow", "250", .250f}, 18 | {"ShortFast", "250", .250f}, {"LongMod", "125", .125f}, {"ShortTurbo", "500", .500f}}; 19 | 20 | const char *LoRaPresets::loRaRegionToString(meshtastic_Config_LoRaConfig_RegionCode region) 21 | { 22 | return regionInfo[region].region; 23 | } 24 | 25 | float LoRaPresets::getFrequencyStart(meshtastic_Config_LoRaConfig_RegionCode region) 26 | { 27 | return regionInfo[region].freqStart; 28 | } 29 | 30 | float LoRaPresets::getFrequencyEnd(meshtastic_Config_LoRaConfig_RegionCode region) 31 | { 32 | return regionInfo[region].freqEnd; 33 | } 34 | 35 | /** 36 | * Default slot number is generated using the same firmware hash algorithm 37 | */ 38 | uint16_t LoRaPresets::getDefaultSlot(meshtastic_Config_LoRaConfig_RegionCode region, 39 | meshtastic_Config_LoRaConfig_ModemPreset preset, const char *channelName) 40 | { 41 | auto hash = [](const char *str) -> uint32_t { 42 | uint32_t hash = 5381; 43 | unsigned char c; 44 | while ((c = *str++) != '\0') 45 | hash += (hash << 5) + c; 46 | return hash; 47 | }; 48 | 49 | uint32_t numChannels = getNumChannels(region, preset); 50 | return numChannels == 0 51 | ? 1 52 | : hash(channelName && channelName[0] != '\0' ? channelName : modemPreset[preset].preset) % numChannels + 1; 53 | } 54 | 55 | float LoRaPresets::getBandwidth(meshtastic_Config_LoRaConfig_ModemPreset preset) 56 | { 57 | // TODO: LORA_24 wide mode (3.25 vs. 31/.03125f, 62/.0625f, 200/.203125f, 400/.40625f, 800/.8125f, 1600/1.6250f 58 | return modemPreset[preset].bandwidth_MHz; 59 | } 60 | 61 | const char *LoRaPresets::getBandwidthString(meshtastic_Config_LoRaConfig_ModemPreset preset) 62 | { 63 | return modemPreset[preset].bandwidth_kHz; 64 | } 65 | 66 | const char *LoRaPresets::modemPresetToString(meshtastic_Config_LoRaConfig_ModemPreset preset) 67 | { 68 | return modemPreset[preset].preset; 69 | } 70 | 71 | uint32_t LoRaPresets::getNumChannels(meshtastic_Config_LoRaConfig_RegionCode region, 72 | meshtastic_Config_LoRaConfig_ModemPreset preset) 73 | { 74 | return (region == meshtastic_Config_LoRaConfig_RegionCode_UNSET 75 | ? 0 76 | : uint32_t((regionInfo[region].freqEnd - regionInfo[region].freqStart) / modemPreset[preset].bandwidth_MHz)); 77 | } 78 | 79 | float LoRaPresets::getRadioFreq(meshtastic_Config_LoRaConfig_RegionCode region, meshtastic_Config_LoRaConfig_ModemPreset preset, 80 | uint32_t channel) 81 | { 82 | return (regionInfo[region].freqStart + modemPreset[preset].bandwidth_MHz / 2) + 83 | (channel - 1) * modemPreset[preset].bandwidth_MHz; 84 | } 85 | -------------------------------------------------------------------------------- /source/graphics/common/ResponseHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/common/ResponseHandler.h" 2 | #include "Arduino.h" 3 | #include "util/ILog.h" 4 | 5 | uint32_t ResponseHandler::rollingPacketId = 0; 6 | 7 | /** 8 | * @brief Construct a new Response Handler:: Response Handler object 9 | * 10 | * @param timeout 11 | */ 12 | ResponseHandler::ResponseHandler(uint32_t timeout) : requestIdCounter(0), maxTime(timeout) 13 | { 14 | rollingPacketId = random(UINT32_MAX & 0x7fffffff); 15 | } 16 | 17 | uint32_t ResponseHandler::addRequest(uint32_t id, RequestType type, void *cookie, Callback cb) 18 | { 19 | requestIdCounter++; 20 | uint32_t requestId = generatePacketId(); 21 | pendingRequest[requestId] = Request{.id = id, .timestamp = millis(), .type = type, .cookie = cookie, .cb = cb}; 22 | return requestId; 23 | } 24 | 25 | ResponseHandler::Request ResponseHandler::findRequest(uint32_t requestId, RequestType match, int32_t pass) 26 | { 27 | const auto it = pendingRequest.find(requestId); 28 | if (it != pendingRequest.end()) { 29 | Request &req = it->second; 30 | if (req.cb && pass != -1 && (match == anyRequest || match == req.type)) 31 | req.cb(req, found, pass); 32 | return req; 33 | } 34 | return Request{}; 35 | } 36 | 37 | ResponseHandler::Request ResponseHandler::removeRequest(uint32_t requestId, RequestType match, int32_t pass) 38 | { 39 | Request req{}; 40 | const auto it = pendingRequest.find(requestId); 41 | if (it != pendingRequest.end()) { 42 | req = it->second; 43 | ILOG_DEBUG("removing request %08x", it->first); 44 | if (req.cb && pass != -1 && (match == anyRequest || match == req.type)) 45 | req.cb(req, removed, pass); 46 | pendingRequest.erase(it); 47 | } 48 | return req; 49 | } 50 | 51 | /** 52 | * @brief: Generate a unique packet id 53 | * 54 | */ 55 | uint32_t ResponseHandler::generatePacketId(void) 56 | { 57 | rollingPacketId++; 58 | rollingPacketId &= UINT32_MAX >> 22; 59 | return rollingPacketId | random(UINT32_MAX & 0x7fffffff) << 10; 60 | } 61 | 62 | /** 63 | * @brief Garbage collection that is periodically called. 64 | * removes all pending requests that timed out 65 | * 66 | */ 67 | void ResponseHandler::task_handler(void) 68 | { 69 | if (pendingRequest.size()) 70 | ILOG_DEBUG("ResponseHandler has %d pending request(s)", pendingRequest.size()); 71 | auto it = pendingRequest.begin(); 72 | while (it != pendingRequest.end()) { 73 | Request &req = it->second; 74 | if (req.timestamp + maxTime < millis()) { 75 | ILOG_DEBUG("removing timed out request %08x", it->first); 76 | if (req.cb) 77 | req.cb(req, timeout, 0); 78 | it = pendingRequest.erase(it); 79 | } else { 80 | it++; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/graphics/common/ViewFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/common/ViewFactory.h" 2 | #if defined(VIEW_128x64) || defined(ARCH_PORTDUINO) 3 | #include "graphics/view/OLED/OLEDView_128x64.h" 4 | #endif 5 | #if defined(VIEW_160x80) || defined(ARCH_PORTDUINO) 6 | #include "graphics/view/TFT/TFTView_160x80.h" 7 | #endif 8 | #if defined(VIEW_240x240) || defined(ARCH_PORTDUINO) 9 | #include "graphics/view/TFT/TFTView_240x240.h" 10 | #endif 11 | #if defined(VIEW_320x240) || defined(ARCH_PORTDUINO) 12 | #include "graphics/view/TFT/TFTView_320x240.h" 13 | #endif 14 | #if defined(VIEW_480x320) || defined(ARCH_PORTDUINO) 15 | #include "graphics/view/TFT/TFTView_480x320.h" 16 | #endif 17 | #include "util/ILog.h" 18 | #include 19 | 20 | ViewFactory::ViewFactory(void) {} 21 | 22 | /** 23 | * @brief Compile-time fix view creation 24 | * 25 | * @return DeviceGUI* 26 | */ 27 | DeviceGUI *ViewFactory::create(void) 28 | { 29 | #if defined(VIEW_128x64) 30 | return OLEDView_128x64::instance(); 31 | #elif defined(VIEW_160x80) 32 | return TFTView_160x80::instance(); 33 | #elif defined(VIEW_240x240) 34 | return TFTView_240x240::instance(); 35 | #elif defined(VIEW_480x320) 36 | return TFTView_480x320::instance(); 37 | #elif defined(VIEW_320x240) 38 | return TFTView_320x240::instance(); 39 | #endif 40 | ILOG_CRIT("ViewFactory: VIEW is not defined and no config provided"); 41 | assert(false); 42 | return nullptr; 43 | } 44 | 45 | /** 46 | * @brief Run-time view creation 47 | * 48 | * @param cfg 49 | * @return DeviceGUI* 50 | */ 51 | DeviceGUI *ViewFactory::create(const DisplayDriverConfig &cfg) 52 | { 53 | #if defined(VIEW_128x64) || defined(ARCH_PORTDUINO) 54 | if (cfg.width() == 128 && cfg.height() == 64) { 55 | return OLEDView_128x64::instance(cfg); 56 | } 57 | #endif 58 | #if defined(VIEW_160x80) || defined(ARCH_PORTDUINO) 59 | if (cfg.width() == 160 && cfg.height() == 80) { 60 | return TFTView_160x80::instance(cfg); 61 | } 62 | #endif 63 | #if defined(VIEW_240x240) || defined(ARCH_PORTDUINO) 64 | if (cfg.width() == 240 && cfg.height() == 240) { 65 | return TFTView_240x240::instance(cfg); 66 | } 67 | #endif 68 | #if defined(VIEW_480x320) || defined(ARCH_PORTDUINO) 69 | if (cfg.width() == 480 && cfg.height() == 320) { 70 | return TFTView_480x320::instance(cfg); 71 | } 72 | #endif 73 | #if defined(VIEW_320x240) || defined(ARCH_PORTDUINO) 74 | // default if nothing else matches 75 | return TFTView_320x240::instance(cfg); 76 | #endif 77 | } 78 | -------------------------------------------------------------------------------- /source/graphics/driver/DisplayDriver.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/driver/DisplayDriver.h" 2 | #include "util/ILog.h" 3 | 4 | #if LV_USE_PROFILER 5 | #if defined(ARCH_PORTDUINO) 6 | #include 7 | #include 8 | #include 9 | #include 10 | #endif 11 | #include "src/misc/lv_profiler_builtin_private.h" 12 | #endif 13 | 14 | DisplayDriver::DisplayDriver(uint16_t width, uint16_t height) 15 | : lvgl(width, height), display(nullptr), touch(nullptr), view(nullptr), screenWidth(width), screenHeight(height) 16 | { 17 | } 18 | 19 | void DisplayDriver::init(DeviceGUI *gui) 20 | { 21 | ILOG_DEBUG("DisplayDriver init..."); 22 | view = gui; 23 | lvgl.init(); 24 | 25 | #if LV_USE_PROFILER 26 | // initialize lvgl profiler 27 | lv_profiler_builtin_config_t config; 28 | lv_profiler_builtin_config_init(&config); 29 | #ifdef ARCH_PORTDUINO 30 | config.tick_per_sec = 1000000000; 31 | config.tick_get_cb = []() -> uint64_t { 32 | struct timespec ts; 33 | clock_gettime(CLOCK_MONOTONIC, &ts); 34 | return ts.tv_sec * 1000000000 + ts.tv_nsec; 35 | }; 36 | config.tid_get_cb = []() -> int { return (int)syscall(SYS_gettid); }; 37 | config.cpu_get_cb = []() -> int { 38 | int cpu_id = 0; 39 | syscall(SYS_getcpu, &cpu_id, NULL); 40 | return cpu_id; 41 | }; 42 | config.flush_cb = [](const char *buf) { Serial.print(buf); }; 43 | #else // arduino 44 | config.tick_per_sec = 1000000; 45 | config.tick_get_cb = []() -> uint64_t { return micros(); }; 46 | config.flush_cb = [](const char *buf) { Serial.println(buf); }; 47 | #endif 48 | lv_profiler_builtin_init(&config); 49 | #endif 50 | } 51 | -------------------------------------------------------------------------------- /source/graphics/driver/DisplayDriverConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/driver/DisplayDriverConfig.h" 2 | 3 | DisplayDriverConfig::DisplayDriverConfig(void) : _device(device_t::NONE), _width(c_default_width), _height(c_default_height) {} 4 | 5 | DisplayDriverConfig::DisplayDriverConfig(enum device_t device, uint16_t width, uint16_t height) 6 | : _device(device), _width(width), _height(height) 7 | { 8 | } 9 | 10 | DisplayDriverConfig::DisplayDriverConfig(struct panel_config_t &&panel, struct bus_config_t &&bus, struct light_config_t &&light, 11 | struct touch_config_t &&touch, struct input_config_t &&input) 12 | : _device(device_t::NONE) 13 | { 14 | } 15 | 16 | DisplayDriverConfig &DisplayDriverConfig::device(enum device_t device) 17 | { 18 | _device = device; 19 | return *this; 20 | } 21 | 22 | DisplayDriverConfig &DisplayDriverConfig::panel(panel_config_t &&cfg) 23 | { 24 | _panel = cfg; 25 | _width = _panel.panel_width; 26 | _height = _panel.panel_height; 27 | return *this; 28 | } 29 | 30 | DisplayDriverConfig &DisplayDriverConfig::bus(bus_config_t &&cfg) 31 | { 32 | _bus = cfg; 33 | return *this; 34 | } 35 | 36 | DisplayDriverConfig &DisplayDriverConfig::touch(touch_config_t &&cfg) 37 | { 38 | _touch = cfg; 39 | return *this; 40 | } 41 | 42 | DisplayDriverConfig &DisplayDriverConfig::input(input_config_t &&cfg) 43 | { 44 | _input = cfg; 45 | return *this; 46 | } 47 | 48 | DisplayDriverConfig &DisplayDriverConfig::light(light_config_t &&cfg) 49 | { 50 | _light = cfg; 51 | return *this; 52 | } 53 | -------------------------------------------------------------------------------- /source/graphics/driver/FBDriver.cpp: -------------------------------------------------------------------------------- 1 | #ifdef USE_FRAMEBUFFER 2 | 3 | #include "graphics/driver/FBDriver.h" 4 | #include "src/core/lv_global.h" 5 | #include "util/ILog.h" 6 | 7 | LV_IMG_DECLARE(mouse_cursor_icon); 8 | 9 | FBDriver *FBDriver::fbDriver = nullptr; 10 | lv_display_t *FBDriver::display = nullptr; 11 | 12 | FBDriver &FBDriver::create(uint16_t width, uint16_t height) 13 | { 14 | if (!fbDriver) 15 | fbDriver = new FBDriver(width, height); 16 | return *fbDriver; 17 | } 18 | 19 | FBDriver::FBDriver(uint16_t width, uint16_t height) : DisplayDriver(width, height) {} 20 | 21 | void FBDriver::init(DeviceGUI *gui) 22 | { 23 | ILOG_DEBUG("FBDriver::init..."); 24 | // Initialize LVGL 25 | DisplayDriver::init(gui); 26 | 27 | // Linux frame buffer device init 28 | const char *device = getenv("LV_LINUX_FBDEV_DEVICE"); 29 | device = (device != nullptr && strlen(device) > 0) ? device : "/dev/fb0"; 30 | display = lv_linux_fbdev_create(); 31 | 32 | if (display == nullptr) { 33 | ILOG_CRIT("Failed to initialize %d", device); 34 | return; 35 | } 36 | lv_linux_fbdev_set_file(display, device); 37 | 38 | #if LV_USE_EVDEV 39 | // discover input devices 40 | lv_evdev_discovery_start(discovery_cb, display); 41 | #endif 42 | } 43 | 44 | #if LV_USE_EVDEV 45 | void FBDriver::discovery_cb(lv_indev_t *indev, lv_evdev_type_t type, void *user_data) 46 | { 47 | ILOG_INFO("'%s' device discovered", type == LV_EVDEV_TYPE_REL ? "REL" 48 | : type == LV_EVDEV_TYPE_ABS ? "ABS" 49 | : type == LV_EVDEV_TYPE_KEY ? "KEY" 50 | : "???"); 51 | 52 | lv_display_t *disp = (lv_display_t *)user_data; 53 | lv_indev_set_display(indev, disp); 54 | 55 | if (type == LV_EVDEV_TYPE_REL) { 56 | set_mouse_cursor_icon(indev, disp); 57 | } 58 | } 59 | 60 | void FBDriver::set_mouse_cursor_icon(lv_indev_t *indev, lv_display_t *display) 61 | { 62 | // Set the cursor icon 63 | LV_IMAGE_DECLARE(mouse_cursor_icon); 64 | lv_obj_t *cursor_obj = lv_image_create(lv_display_get_screen_active(display)); 65 | lv_image_set_src(cursor_obj, &mouse_cursor_icon); 66 | lv_indev_set_cursor(indev, cursor_obj); 67 | 68 | // delete the mouse cursor icon if the device is removed 69 | lv_indev_add_event_cb(indev, indev_deleted_cb, LV_EVENT_DELETE, cursor_obj); 70 | } 71 | 72 | void FBDriver::indev_deleted_cb(lv_event_t *e) 73 | { 74 | if (LV_GLOBAL_DEFAULT()->deinit_in_progress) 75 | return; 76 | lv_obj_t *cursor_obj = (lv_obj_t *)lv_event_get_user_data(e); 77 | lv_obj_del(cursor_obj); 78 | } 79 | #endif 80 | 81 | #endif -------------------------------------------------------------------------------- /source/graphics/driver/X11Driver.cpp: -------------------------------------------------------------------------------- 1 | #ifdef USE_X11 2 | #include "graphics/driver/X11Driver.h" 3 | #include "util/ILog.h" 4 | #include 5 | 6 | LV_IMG_DECLARE(mouse_cursor_icon); 7 | 8 | X11Driver *X11Driver::x11driver = nullptr; 9 | 10 | X11Driver &X11Driver::create(uint16_t width, uint16_t height) 11 | { 12 | if (!x11driver) 13 | x11driver = new X11Driver(width, height); 14 | return *x11driver; 15 | } 16 | 17 | X11Driver::X11Driver(uint16_t width, uint16_t height) : DisplayDriver(width, height) {} 18 | 19 | void X11Driver::init(DeviceGUI *gui) 20 | { 21 | ILOG_DEBUG("X11Driver::init..."); 22 | // Initialize LVGL 23 | DisplayDriver::init(gui); 24 | 25 | char title[25]; 26 | sprintf(title, "Meshtastic (%dx%d)", screenWidth, screenHeight); 27 | display = lv_x11_window_create(title, screenWidth, screenHeight); 28 | lv_x11_inputs_create(display, &mouse_cursor_icon); 29 | } 30 | 31 | #endif -------------------------------------------------------------------------------- /source/graphics/map/FileSystemService.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/FileSystemService.h" 2 | #include "FS.h" 3 | #include "graphics/map/MapTileSettings.h" 4 | #include "lvgl.h" 5 | #include "screens.h" 6 | #include "util/ILog.h" 7 | #include 8 | #include 9 | 10 | #define DRIVE_LETTER "A" 11 | 12 | LV_IMAGE_DECLARE(img_no_tile_image); 13 | 14 | FileSystemService::FileSystemService() : ITileService(DRIVE_LETTER ":") 15 | { 16 | static lv_fs_drv_t drv; 17 | lv_fs_drv_init(&drv); 18 | drv.letter = DRIVE_LETTER[0]; 19 | drv.cache_size = MapTileSettings::getCacheSize(); 20 | drv.ready_cb = nullptr; 21 | drv.open_cb = fs_open; 22 | drv.close_cb = fs_close; 23 | drv.read_cb = fs_read; 24 | drv.write_cb = fs_write; 25 | drv.seek_cb = fs_seek; 26 | drv.tell_cb = fs_tell; 27 | lv_fs_drv_register(&drv); 28 | } 29 | 30 | FileSystemService::~FileSystemService() {} 31 | 32 | bool FileSystemService::load(const char *name, void *img) 33 | { 34 | char buf[128] = DRIVE_LETTER ":"; 35 | strcat(&buf[2], name); 36 | lv_image_set_src((lv_obj_t *)img, buf); 37 | if (!lv_image_get_src((lv_obj_t *)img)) { 38 | lv_image_set_src((lv_obj_t *)img, &img_no_tile_image); 39 | ILOG_WARN("*** Failed to load tile %s", buf); 40 | return false; 41 | } 42 | ILOG_INFO("*** Tile %s loaded.", buf); 43 | return true; 44 | } 45 | 46 | void *FileSystemService::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 47 | { 48 | ILOG_DEBUG("fs_open %s", path); 49 | return fopen(path, mode == LV_FS_MODE_RD ? "rb" : "wb"); 50 | } 51 | 52 | lv_fs_res_t FileSystemService::fs_close(lv_fs_drv_t *drv, void *file_p) 53 | { 54 | ILOG_DEBUG("fs_close()"); 55 | return fclose((FILE *)file_p) != 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 56 | } 57 | 58 | lv_fs_res_t FileSystemService::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 59 | { 60 | *br = fread(buf, 1, btr, (FILE *)file_p); 61 | ILOG_DEBUG("fs_read(): %d/%d bytes", *br, btr); 62 | return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 63 | } 64 | 65 | lv_fs_res_t FileSystemService::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 66 | { 67 | *bw = fwrite(buf, 1, btw, (FILE *)file_p); 68 | ILOG_DEBUG("fs_write(): %d/%d bytes", *bw, btw); 69 | return (*bw <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 70 | } 71 | 72 | lv_fs_res_t FileSystemService::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 73 | { 74 | ILOG_DEBUG("fs_seek(): pos %d", pos); 75 | return fseek((FILE *)file_p, pos, whence) != 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 76 | } 77 | 78 | lv_fs_res_t FileSystemService::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 79 | { 80 | *pos_p = ftell((FILE *)file_p); 81 | ILOG_DEBUG("fs_tell(): pos %d", *pos_p); 82 | return LV_FS_RES_OK; 83 | } 84 | -------------------------------------------------------------------------------- /source/graphics/map/LinuxFileSystemService.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/LinuxFileSystemService.h" 2 | #include "graphics/map/MapTileSettings.h" 3 | #include "lvgl.h" 4 | #include "screens.h" 5 | #include "util/ILog.h" 6 | #include 7 | #include 8 | 9 | #define DRIVE_LETTER "F" 10 | 11 | LV_IMAGE_DECLARE(img_no_tile_image); 12 | 13 | LinuxFileSystemService::LinuxFileSystemService() : ITileService(DRIVE_LETTER ":") 14 | { 15 | static lv_fs_drv_t drv; 16 | lv_fs_drv_init(&drv); 17 | drv.letter = DRIVE_LETTER[0]; 18 | drv.cache_size = MapTileSettings::getCacheSize(); 19 | drv.ready_cb = nullptr; 20 | drv.open_cb = fs_open; 21 | drv.close_cb = fs_close; 22 | drv.read_cb = fs_read; 23 | drv.write_cb = fs_write; 24 | drv.seek_cb = fs_seek; 25 | drv.tell_cb = fs_tell; 26 | lv_fs_drv_register(&drv); 27 | } 28 | 29 | LinuxFileSystemService::~LinuxFileSystemService() {} 30 | 31 | bool LinuxFileSystemService::load(const char *name, void *img) 32 | { 33 | char buf[128] = DRIVE_LETTER ":"; 34 | strcat(&buf[2], name); 35 | lv_image_set_src((lv_obj_t *)img, buf); 36 | if (!lv_image_get_src((lv_obj_t *)img)) { 37 | lv_image_set_src((lv_obj_t *)img, &img_no_tile_image); 38 | ILOG_WARN("failed to load tile %s", buf); 39 | return false; 40 | } 41 | ILOG_DEBUG("tile %s loaded", buf); 42 | return true; 43 | } 44 | 45 | void *LinuxFileSystemService::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 46 | { 47 | // ILOG_DEBUG("fs_open %s", path); 48 | return fopen(path, mode == LV_FS_MODE_RD ? "rb" : "wb"); 49 | } 50 | 51 | lv_fs_res_t LinuxFileSystemService::fs_close(lv_fs_drv_t *drv, void *file_p) 52 | { 53 | // ILOG_DEBUG("fs_close()"); 54 | return fclose((FILE *)file_p) != 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 55 | } 56 | 57 | lv_fs_res_t LinuxFileSystemService::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 58 | { 59 | *br = fread(buf, 1, btr, (FILE *)file_p); 60 | // ILOG_DEBUG("fs_read(): %d bytes", *br); 61 | return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 62 | } 63 | 64 | lv_fs_res_t LinuxFileSystemService::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 65 | { 66 | *bw = fwrite(buf, 1, btw, (FILE *)file_p); 67 | // ILOG_DEBUG("fs_write(): %d bytes", *bw); 68 | return (*bw <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 69 | } 70 | 71 | lv_fs_res_t LinuxFileSystemService::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 72 | { 73 | // ILOG_DEBUG("fs_seek(): pos %d", pos); 74 | return fseek((FILE *)file_p, pos, whence) != 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 75 | } 76 | 77 | lv_fs_res_t LinuxFileSystemService::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 78 | { 79 | *pos_p = ftell((FILE *)file_p); 80 | // ILOG_DEBUG("fs_tell(): pos %d", *pos_p); 81 | return LV_FS_RES_OK; 82 | } 83 | -------------------------------------------------------------------------------- /source/graphics/map/MapTile.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/MapTile.h" 2 | #include "graphics/map/MapTileSettings.h" 3 | #include "graphics/map/TileService.h" 4 | #include "lvgl.h" 5 | #include "util/ILog.h" 6 | 7 | #include 8 | 9 | LV_IMAGE_DECLARE(img_no_tile_image); 10 | 11 | OSMTiles *osm = nullptr; 12 | 13 | MapTile::MapTile(uint32_t xTile, uint32_t yTile) 14 | : OSMTiles::Tile(xTile, yTile, MapTileSettings::getZoomLevel()), img(nullptr), lbl(nullptr) 15 | { 16 | // singleton should be already created 17 | assert(osm != nullptr); 18 | } 19 | 20 | /** 21 | * load map tile to display position x/y 22 | */ 23 | bool MapTile::load(lv_obj_t *p, int16_t posx, int16_t posy, const lv_image_dsc_t *img_src) 24 | { 25 | x = posx; 26 | y = posy; 27 | if (!p) 28 | return false; 29 | removeImage(); 30 | img = lv_image_create(p); 31 | lv_obj_set_pos(img, posx, posy); 32 | lv_obj_set_style_opa(img, 255, LV_PART_MAIN | LV_STATE_DEFAULT); 33 | lv_obj_set_size(img, MapTileSettings::getTileSize(), MapTileSettings::getTileSize()); 34 | if (MapTileSettings::getDebug()) { 35 | lv_obj_set_style_border_width(img, 1, LV_PART_MAIN | LV_STATE_DEFAULT); 36 | lbl = lv_label_create(img); 37 | lv_obj_set_pos(lbl, 0, 0); 38 | lv_obj_set_size(lbl, LV_SIZE_CONTENT, LV_SIZE_CONTENT); 39 | lv_obj_set_style_text_color(lbl, lv_color_hex(0xff101010), LV_PART_MAIN | LV_STATE_DEFAULT); 40 | lv_label_set_text_fmt(lbl, "(%d/%d/%d) -> %d,%d", MapTileSettings::getZoomLevel(), xTile, yTile, posx, posy); 41 | } 42 | 43 | bool result = false; 44 | #if LV_USE_FS_ARDUINO_SD 45 | // use lvgl built-in img loader 46 | char fname[128]; 47 | fname[0] = LV_FS_ARDUINO_SD_LETTER; 48 | sprintf(&fname[1], ":%s/%s%d/%d/%d.%s", MapTileSettings::getPrefix(), MapTileSettings::getTileStyle(), zoomLevel, xTile, 49 | yTile, MapTileSettings::getTileFormat()); 50 | ILOG_DEBUG("SD file: %s", fname); 51 | lv_image_set_src(img, fname); 52 | if (lv_image_get_src((lv_obj_t *)img)) { 53 | result = true; 54 | } 55 | #endif 56 | // use configured TileService 57 | if (!result) { 58 | result = osm->load(*this, img); 59 | if (!result) { 60 | if (img_src) { 61 | lv_image_set_src((lv_obj_t *)img, img_src); 62 | lv_obj_set_style_opa(img, 100, LV_PART_MAIN | LV_STATE_DEFAULT); 63 | if (!MapTileSettings::getDebug()) { 64 | lv_obj_t *lbl = lv_label_create(img); 65 | lv_obj_set_pos(lbl, 0, 50); 66 | lv_obj_set_align(lbl, LV_ALIGN_CENTER); 67 | lv_obj_set_size(lbl, LV_SIZE_CONTENT, LV_SIZE_CONTENT); 68 | lv_obj_set_style_text_color(lbl, lv_color_hex(0xff505050), LV_PART_MAIN | LV_STATE_DEFAULT); 69 | lv_label_set_text_fmt(lbl, "(%d/%d/%d)", MapTileSettings::getZoomLevel(), xTile, yTile); 70 | } 71 | } 72 | } 73 | } 74 | return result; 75 | } 76 | 77 | bool MapTile::move(int16_t posx, int16_t posy) 78 | { 79 | x += posx; 80 | y += posy; 81 | if (img) 82 | lv_obj_set_pos(img, x, y); 83 | if (MapTileSettings::getDebug()) { 84 | lv_label_set_text_fmt(lbl, "(%d/%d/%d) -> %d,%d", MapTileSettings::getZoomLevel(), xTile, yTile, x, y); 85 | } 86 | return true; 87 | } 88 | 89 | void MapTile::removeImage(void) 90 | { 91 | if (img) { 92 | // ILOG_DEBUG("remove image %d/%d", xTile, yTile); 93 | lv_obj_delete(img); 94 | img = nullptr; 95 | } 96 | } 97 | 98 | MapTile::~MapTile() 99 | { 100 | // ILOG_DEBUG("MapTile::~MapTile %d/%d/%d", zoomLevel, xTile, yTile); 101 | removeImage(); 102 | } 103 | -------------------------------------------------------------------------------- /source/graphics/map/MapTileSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/MapTileSettings.h" 2 | #include "lv_conf.h" 3 | #include "lvgl.h" 4 | 5 | uint8_t MapTileSettings::zoomLevel = 13; // current zoomLevel 6 | uint8_t MapTileSettings::zoomDefault = 13; // default for initial or home position 7 | uint16_t MapTileSettings::tileSize = 256; 8 | uint32_t MapTileSettings::cacheSize = 50 * 1024; // LV_FS_CACHE_FROM_BUFFER 9 | float MapTileSettings::defaultLat = 51.5003646652f; // @theBigBentern 10 | float MapTileSettings::defaultLon = -0.1214328476f; 11 | char MapTileSettings::prefix[10] = "/maps"; // default map tile directory 12 | char MapTileSettings::tileStyle[20] = ""; // { osm/, atlas/, atlas-mobile/, ...} 13 | char MapTileSettings::tileFormat[10] = "png"; // use jpg or png 14 | bool MapTileSettings::debug = false; -------------------------------------------------------------------------------- /source/graphics/map/SDCardService.cpp: -------------------------------------------------------------------------------- 1 | #include "lvgl.h" 2 | 3 | #include "graphics/map/MapTileSettings.h" 4 | #include "graphics/map/SDCardService.h" 5 | #include "util/ILog.h" 6 | 7 | #ifdef ARCH_PORTDUINO 8 | #include "PortduinoFS.h" 9 | static fs::FS &SD = PortduinoFS; // Portduino does not (yet) support SD device, use normal file system 10 | #elif defined(HAS_SD_MMC) 11 | #include "SD_MMC.h" 12 | static fs::SDMMCFS &SD = SD_MMC; 13 | #else 14 | #include "SD.h" 15 | #endif 16 | 17 | #define DRIVE_LETTER "S" 18 | 19 | SDCardService::SDCardService() : ITileService(DRIVE_LETTER ":") 20 | { 21 | static lv_fs_drv_t drv; 22 | lv_fs_drv_init(&drv); 23 | drv.letter = DRIVE_LETTER[0]; 24 | drv.cache_size = MapTileSettings::getCacheSize(); 25 | drv.ready_cb = nullptr; 26 | drv.open_cb = fs_open; 27 | drv.close_cb = fs_close; 28 | drv.read_cb = fs_read; 29 | drv.write_cb = fs_write; 30 | drv.seek_cb = fs_seek; 31 | drv.tell_cb = fs_tell; 32 | drv.dir_open_cb = fs_dir_open; 33 | drv.dir_read_cb = fs_dir_read; 34 | drv.dir_close_cb = fs_dir_close; 35 | lv_fs_drv_register(&drv); 36 | } 37 | 38 | SDCardService::~SDCardService() 39 | { 40 | #ifndef ARCH_PORTDUINO 41 | SD.end(); 42 | #endif 43 | } 44 | 45 | bool SDCardService::load(const char *name, void *img) 46 | { 47 | char buf[128] = DRIVE_LETTER ":"; 48 | strcat(&buf[2], name); 49 | ILOG_DEBUG("SDCardService::load(): %s", buf); 50 | lv_image_set_src((lv_obj_t *)img, buf); 51 | if (!lv_image_get_src((lv_obj_t *)img)) { 52 | ILOG_DEBUG("Failed to load tile %s from SD", buf); 53 | return false; 54 | } 55 | // ILOG_INFO("*** Tile %s loaded.", buf); 56 | return true; 57 | } 58 | 59 | void *SDCardService::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 60 | { 61 | String s(path); 62 | File file = SD.open(path, mode == LV_FS_MODE_RD ? FILE_READ : FILE_WRITE); 63 | if (!file) { 64 | // ILOG_WARN("SD.open() %s failed!", path); 65 | return nullptr; 66 | } else { 67 | // ILOG_DEBUG("SD.open() %s ok", path); 68 | SdFile *lf = new SdFile{file}; 69 | return static_cast(lf); 70 | } 71 | } 72 | 73 | lv_fs_res_t SDCardService::fs_close(lv_fs_drv_t *drv, void *file_p) 74 | { 75 | // ILOG_DEBUG("SD.close()"); 76 | SdFile *lf = static_cast(file_p); 77 | lf->file.close(); 78 | delete lf; 79 | return LV_FS_RES_OK; 80 | } 81 | 82 | lv_fs_res_t SDCardService::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 83 | { 84 | *br = static_cast(file_p)->file.read((uint8_t *)buf, btr); 85 | // ILOG_DEBUG("SD.read(): %d/%d bytes", *br, btr); 86 | return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 87 | } 88 | 89 | lv_fs_res_t SDCardService::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 90 | { 91 | *bw = static_cast(file_p)->file.write((uint8_t *)buf, btw); 92 | // ILOG_DEBUG("SD.write(): %d/btw bytes", *bw, btw); 93 | return (*bw <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 94 | } 95 | 96 | lv_fs_res_t SDCardService::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 97 | { 98 | // ILOG_DEBUG("SD.seek(): pos %d", pos); 99 | return static_cast(file_p)->file.seek(pos, (SeekMode)whence) ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; 100 | } 101 | 102 | lv_fs_res_t SDCardService::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 103 | { 104 | *pos_p = static_cast(file_p)->file.position(); 105 | // ILOG_DEBUG("SD.tell(): pos %d", *pos_p); 106 | return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 107 | } 108 | 109 | void *SDCardService::fs_dir_open(lv_fs_drv_t *drv, const char *path) 110 | { 111 | return nullptr; // TODO 112 | } 113 | 114 | lv_fs_res_t SDCardService::fs_dir_read(lv_fs_drv_t *drv, void *rddir_p, char *fn, uint32_t fn_len) 115 | { 116 | return LV_FS_RES_NOT_IMP; // TODO 117 | } 118 | 119 | lv_fs_res_t SDCardService::fs_dir_close(lv_fs_drv_t *drv, void *rddir_p) 120 | { 121 | return LV_FS_RES_NOT_IMP; // TODO 122 | } 123 | -------------------------------------------------------------------------------- /source/graphics/map/SdFatService.cpp: -------------------------------------------------------------------------------- 1 | #if defined(HAS_SDCARD) && not defined(HAS_SD_MMC) && not defined(ARCH_PORTDUINO) 2 | 3 | #include "lvgl.h" 4 | 5 | #include "graphics/common/SdCard.h" 6 | #include "graphics/map/MapTileSettings.h" 7 | #include "graphics/map/SdFatService.h" 8 | #include "util/ILog.h" 9 | #include 10 | 11 | #define DRIVE_LETTER "S" 12 | 13 | SdFatService::SdFatService() : ITileService(DRIVE_LETTER ":") 14 | { 15 | static lv_fs_drv_t drv; 16 | lv_fs_drv_init(&drv); 17 | drv.letter = DRIVE_LETTER[0]; 18 | drv.cache_size = MapTileSettings::getCacheSize(); 19 | drv.ready_cb = nullptr; 20 | drv.open_cb = fs_open; 21 | drv.close_cb = fs_close; 22 | drv.read_cb = fs_read; 23 | drv.write_cb = fs_write; 24 | drv.seek_cb = fs_seek; 25 | drv.tell_cb = fs_tell; 26 | drv.dir_open_cb = fs_dir_open; 27 | drv.dir_read_cb = fs_dir_read; 28 | drv.dir_close_cb = fs_dir_close; 29 | lv_fs_drv_register(&drv); 30 | } 31 | 32 | SdFatService::~SdFatService() 33 | { 34 | SDFs.end(); 35 | } 36 | 37 | bool SdFatService::load(const char *name, void *img) 38 | { 39 | char buf[128] = DRIVE_LETTER ":"; 40 | strcat(&buf[2], name); 41 | // ILOG_DEBUG("SdFatService::load(): %s", buf); 42 | lv_image_set_src((lv_obj_t *)img, buf); 43 | if (!lv_image_get_src((lv_obj_t *)img)) { 44 | ILOG_DEBUG("Failed to load tile %s from SD", buf); 45 | return false; 46 | } 47 | // ILOG_INFO("*** Tile %s loaded.", buf); 48 | return true; 49 | } 50 | 51 | void *SdFatService::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 52 | { 53 | String s(path); 54 | SdFile *lf = new SdFile; 55 | lf->file = SDFs.open(path, mode == LV_FS_MODE_RD ? O_RDONLY : O_WRONLY); // NOTE: O_RDWR 56 | if (!lf->file) { 57 | // ILOG_DEBUG("FsSD.open() %s failed!", path); 58 | delete lf; 59 | return nullptr; 60 | } else { 61 | return static_cast(lf); 62 | } 63 | } 64 | 65 | lv_fs_res_t SdFatService::fs_close(lv_fs_drv_t *drv, void *file_p) 66 | { 67 | // ILOG_DEBUG("FsSD.close()"); 68 | SdFile *lf = static_cast(file_p); 69 | lf->file.close(); 70 | delete lf; 71 | return LV_FS_RES_OK; 72 | } 73 | 74 | lv_fs_res_t SdFatService::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 75 | { 76 | *br = static_cast(file_p)->file.read((uint8_t *)buf, btr); 77 | // ILOG_DEBUG("FsSD.read(): %d/%d bytes", *br, btr); 78 | return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 79 | } 80 | 81 | lv_fs_res_t SdFatService::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 82 | { 83 | *bw = static_cast(file_p)->file.write((uint8_t *)buf, btw); 84 | // ILOG_DEBUG("FsSD.write(): %d/btw bytes", *bw, btw); 85 | return (*bw <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 86 | } 87 | 88 | lv_fs_res_t SdFatService::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 89 | { 90 | // ILOG_DEBUG("FsSD.seek(): pos %d", pos); 91 | if (whence == LV_FS_SEEK_SET) { 92 | return static_cast(file_p)->file.seekSet(pos) ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; 93 | } else if (whence == LV_FS_SEEK_END) { 94 | return static_cast(file_p)->file.seekEnd() ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; 95 | } else { 96 | return static_cast(file_p)->file.seekCur(pos) ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; 97 | } 98 | } 99 | 100 | lv_fs_res_t SdFatService::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 101 | { 102 | *pos_p = static_cast(file_p)->file.position(); 103 | // ILOG_DEBUG("FsSD.tell(): pos %d", *pos_p); 104 | return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 105 | } 106 | 107 | void *SdFatService::fs_dir_open(lv_fs_drv_t *drv, const char *path) 108 | { 109 | return nullptr; // TODO 110 | } 111 | 112 | lv_fs_res_t SdFatService::fs_dir_read(lv_fs_drv_t *drv, void *rddir_p, char *fn, uint32_t fn_len) 113 | { 114 | return LV_FS_RES_NOT_IMP; // TODO 115 | } 116 | 117 | lv_fs_res_t SdFatService::fs_dir_close(lv_fs_drv_t *drv, void *rddir_p) 118 | { 119 | return LV_FS_RES_NOT_IMP; // TODO 120 | } 121 | 122 | #endif -------------------------------------------------------------------------------- /source/graphics/map/TileService.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/TileService.h" 2 | 3 | void TileService::setService(ITileService *s) 4 | { 5 | delete service; 6 | service = s; 7 | } 8 | 9 | void TileService::setBackupService(ITileService *s) 10 | { 11 | delete backup; 12 | backup = s; 13 | } 14 | 15 | TileService::~TileService() 16 | { 17 | delete service; 18 | delete backup; 19 | } -------------------------------------------------------------------------------- /source/graphics/map/URLService.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/URLService.h" 2 | #include "graphics/map/MapTileSettings.h" 3 | 4 | #if 0 // clashes with wifi client in firmware and portduino layer 5 | #include 6 | #include 7 | #include 8 | #endif 9 | 10 | URLService::URLService() : ITileService("U:") 11 | { 12 | static lv_fs_drv_t drv; 13 | lv_fs_drv_init(&drv); 14 | drv.letter = 'U'; 15 | drv.cache_size = MapTileSettings::getCacheSize(); 16 | drv.ready_cb = nullptr; 17 | drv.open_cb = fs_open; 18 | drv.close_cb = fs_close; 19 | drv.read_cb = fs_read; 20 | drv.write_cb = fs_write; 21 | drv.seek_cb = fs_seek; 22 | drv.tell_cb = fs_tell; 23 | lv_fs_drv_register(&drv); 24 | } 25 | 26 | URLService::~URLService() {} 27 | 28 | bool URLService::load(const char *name, void *img) 29 | { 30 | return false; // TODO 31 | } 32 | 33 | void *URLService::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 34 | { 35 | return nullptr; // TODO 36 | } 37 | 38 | lv_fs_res_t URLService::fs_close(lv_fs_drv_t *drv, void *file_p) 39 | { 40 | return LV_FS_RES_NOT_IMP; 41 | } 42 | 43 | lv_fs_res_t URLService::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 44 | { 45 | return LV_FS_RES_NOT_IMP; 46 | } 47 | 48 | lv_fs_res_t URLService::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 49 | { 50 | return LV_FS_RES_NOT_IMP; 51 | } 52 | 53 | lv_fs_res_t URLService::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 54 | { 55 | return LV_FS_RES_NOT_IMP; 56 | } 57 | 58 | lv_fs_res_t URLService::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 59 | { 60 | return LV_FS_RES_NOT_IMP; 61 | } 62 | -------------------------------------------------------------------------------- /source/input/ButtonInputDriver.cpp: -------------------------------------------------------------------------------- 1 | #ifdef INPUTDRIVER_BUTTON_TYPE 2 | 3 | #include "input/ButtonInputDriver.h" 4 | #include "util/ILog.h" 5 | #include 6 | 7 | ButtonInputDriver::ButtonInputDriver(void) {} 8 | 9 | void ButtonInputDriver::init(void) 10 | { 11 | button = lv_indev_create(); 12 | lv_indev_set_type(button, LV_INDEV_TYPE_BUTTON); 13 | lv_indev_set_read_cb(button, button_read); 14 | 15 | // assign buttons to points on the screen 16 | static const lv_point_t btn_points[1] = { 17 | {10, 235} // button0: (cog symbol) and button on blank screen 18 | }; 19 | 20 | lv_indev_set_button_points(button, btn_points); 21 | 22 | if (!inputGroup) { 23 | inputGroup = lv_group_create(); 24 | lv_group_set_default(inputGroup); 25 | } 26 | lv_indev_set_group(button, inputGroup); 27 | lv_indev_enable(button, false); 28 | 29 | pinMode(INPUTDRIVER_BUTTON_TYPE, INPUT); 30 | } 31 | 32 | void ButtonInputDriver::button_read(lv_indev_t *indev, lv_indev_data_t *data) 33 | { 34 | static uint8_t lastBtn = 0; 35 | int8_t actBtn = getButtonPressedId(); 36 | if (actBtn >= 0) { 37 | data->state = LV_INDEV_STATE_PRESSED; 38 | lastBtn = actBtn; 39 | } else { 40 | data->state = LV_INDEV_STATE_RELEASED; 41 | } 42 | 43 | data->btn_id = lastBtn; 44 | } 45 | 46 | int8_t ButtonInputDriver::getButtonPressedId(void) 47 | { 48 | if (!digitalRead(INPUTDRIVER_BUTTON_TYPE)) 49 | return 0; 50 | else 51 | return -1; 52 | } 53 | 54 | #endif -------------------------------------------------------------------------------- /source/input/I2CKeyboardInputDriver.cpp: -------------------------------------------------------------------------------- 1 | #ifdef INPUTDRIVER_I2C_KBD_TYPE 2 | 3 | #include "input/I2CKeyboardInputDriver.h" 4 | #include "util/ILog.h" 5 | #include 6 | #include 7 | 8 | I2CKeyboardInputDriver::I2CKeyboardInputDriver(void) {} 9 | 10 | void I2CKeyboardInputDriver::init(void) 11 | { 12 | keyboard = lv_indev_create(); 13 | lv_indev_set_type(keyboard, LV_INDEV_TYPE_KEYPAD); 14 | lv_indev_set_read_cb(keyboard, keyboard_read); 15 | 16 | if (!inputGroup) { 17 | inputGroup = lv_group_create(); 18 | lv_group_set_default(inputGroup); 19 | } 20 | lv_indev_set_group(keyboard, inputGroup); 21 | } 22 | 23 | /****************************************************************** 24 | LV_KEY_NEXT: Focus on the next object 25 | LV_KEY_PREV: Focus on the previous object 26 | LV_KEY_ENTER: Triggers LV_EVENT_PRESSED, LV_EVENT_CLICKED, or LV_EVENT_LONG_PRESSED etc. events 27 | LV_KEY_UP: Increase value or move upwards 28 | LV_KEY_DOWN: Decrease value or move downwards 29 | LV_KEY_RIGHT: Increase value or move to the right 30 | LV_KEY_LEFT: Decrease value or move to the left 31 | LV_KEY_ESC: Close or exit (E.g. close a Drop down list) 32 | LV_KEY_DEL: Delete (E.g. a character on the right in a Text area) 33 | LV_KEY_BACKSPACE: Delete a character on the left (E.g. in a Text area) 34 | LV_KEY_HOME: Go to the beginning/top (E.g. in a Text area) 35 | LV_KEY_END: Go to the end (E.g. in a Text area) 36 | 37 | LV_KEY_UP = 17, // 0x11 38 | LV_KEY_DOWN = 18, // 0x12 39 | LV_KEY_RIGHT = 19, // 0x13 40 | LV_KEY_LEFT = 20, // 0x14 41 | LV_KEY_ESC = 27, // 0x1B 42 | LV_KEY_DEL = 127, // 0x7F 43 | LV_KEY_BACKSPACE = 8, // 0x08 44 | LV_KEY_ENTER = 10, // 0x0A, '\n' 45 | LV_KEY_NEXT = 9, // 0x09, '\t' 46 | LV_KEY_PREV = 11, // 0x0B, ' 47 | LV_KEY_HOME = 2, // 0x02, STX 48 | LV_KEY_END = 3, // 0x03, ETX 49 | *******************************************************************/ 50 | 51 | void I2CKeyboardInputDriver::keyboard_read(lv_indev_t *indev, lv_indev_data_t *data) 52 | { 53 | char keyValue = 0; 54 | Wire.requestFrom(INPUTDRIVER_I2C_KBD_TYPE, 1); 55 | if (Wire.available() > 0) { 56 | keyValue = Wire.read(); 57 | // ignore empty reads and keycode 224(E0, shift-0 on T-Deck) which causes internal issues 58 | if (keyValue != (char)0x00 && keyValue != (char)0xE0) { 59 | data->state = LV_INDEV_STATE_PRESSED; 60 | ILOG_DEBUG("key press value: %d", (int)keyValue); 61 | 62 | switch (keyValue) { 63 | case 0x0D: 64 | keyValue = LV_KEY_ENTER; 65 | break; 66 | default: 67 | break; 68 | } 69 | } else { 70 | data->state = LV_INDEV_STATE_RELEASED; 71 | } 72 | } 73 | data->key = (uint32_t)keyValue; 74 | } 75 | 76 | void I2CKeyboardInputDriver::task_handler(void) {} 77 | 78 | I2CKeyboardInputDriver::~I2CKeyboardInputDriver(void) {} 79 | 80 | #endif -------------------------------------------------------------------------------- /source/input/InputDriver.cpp: -------------------------------------------------------------------------------- 1 | #include "input/InputDriver.h" 2 | 3 | InputDriver *InputDriver::driver = nullptr; 4 | lv_indev_t *InputDriver::keyboard = nullptr; 5 | lv_indev_t *InputDriver::pointer = nullptr; 6 | lv_indev_t *InputDriver::encoder = nullptr; 7 | lv_indev_t *InputDriver::button = nullptr; 8 | lv_group_t *InputDriver::inputGroup = nullptr; 9 | 10 | InputDriver *InputDriver::instance(void) 11 | { 12 | if (!driver) 13 | driver = new InputDriver; 14 | return driver; 15 | } 16 | 17 | InputDriver::~InputDriver(void) 18 | { 19 | if (keyboard) 20 | releaseKeyboardDevice(); 21 | if (pointer) 22 | releasePointerDevice(); 23 | } 24 | -------------------------------------------------------------------------------- /source/util/FileLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "util/FileLoader.h" 2 | #include "lvgl_private.h" 3 | #include "util/ILog.h" 4 | 5 | fs::FS *FileLoader::_fs = nullptr; 6 | 7 | void FileLoader::init(fs::FS *fs) 8 | { 9 | _fs = fs; 10 | static lv_fs_drv_t drv; 11 | lv_fs_drv_init(&drv); 12 | 13 | drv.letter = FL_DRIVE_LETTER[0]; 14 | drv.cache_size = 32768; 15 | drv.ready_cb = nullptr; 16 | drv.open_cb = fs_open; 17 | drv.close_cb = fs_close; 18 | drv.read_cb = fs_read; 19 | drv.write_cb = fs_write; 20 | drv.seek_cb = fs_seek; 21 | drv.tell_cb = fs_tell; 22 | drv.dir_open_cb = nullptr; 23 | drv.dir_read_cb = nullptr; 24 | drv.dir_close_cb = nullptr; 25 | lv_fs_drv_register(&drv); 26 | } 27 | 28 | void *FileLoader::fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) 29 | { 30 | File file = _fs->open(path, mode == LV_FS_MODE_RD ? FILE_READ : FILE_WRITE); 31 | if (!file) { 32 | return nullptr; 33 | } else { 34 | FileHandle *lf = new FileHandle{file}; 35 | return static_cast(lf); 36 | } 37 | } 38 | 39 | lv_fs_res_t FileLoader::fs_close(lv_fs_drv_t *drv, void *file_p) 40 | { 41 | if (file_p == nullptr) { 42 | return LV_FS_RES_INV_PARAM; 43 | } 44 | FileHandle *lf = static_cast(file_p); 45 | if (lf->file) { 46 | lf->file.close(); 47 | delete lf; 48 | } 49 | return LV_FS_RES_OK; 50 | } 51 | 52 | lv_fs_res_t FileLoader::fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) 53 | { 54 | if (file_p == nullptr) { 55 | return LV_FS_RES_INV_PARAM; 56 | } 57 | *br = static_cast(file_p)->file.read((uint8_t *)buf, btr); 58 | return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 59 | } 60 | 61 | lv_fs_res_t FileLoader::fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) 62 | { 63 | if (file_p == nullptr) { 64 | return LV_FS_RES_INV_PARAM; 65 | } 66 | *bw = static_cast(file_p)->file.write((uint8_t *)buf, btw); 67 | return (*bw <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 68 | } 69 | 70 | lv_fs_res_t FileLoader::fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) 71 | { 72 | if (file_p == nullptr) { 73 | return LV_FS_RES_INV_PARAM; 74 | } 75 | return static_cast(file_p)->file.seek(pos, (SeekMode)whence) ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; 76 | } 77 | 78 | lv_fs_res_t FileLoader::fs_size(lv_fs_drv_t *drv, void *file_p, uint32_t *size_p) 79 | { 80 | if (file_p == nullptr) { 81 | return LV_FS_RES_INV_PARAM; 82 | } 83 | *size_p = static_cast(file_p)->file.size(); 84 | return LV_FS_RES_OK; 85 | } 86 | 87 | lv_fs_res_t FileLoader::fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) 88 | { 89 | if (file_p == nullptr) { 90 | return LV_FS_RES_INV_PARAM; 91 | } 92 | *pos_p = static_cast(file_p)->file.position(); 93 | return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; 94 | } 95 | 96 | bool FileLoader::loadImage(lv_obj_t *img, const char *path) 97 | { 98 | lv_image_set_src(img, path); 99 | return lv_image_get_src((lv_obj_t *)img) != nullptr; 100 | } 101 | 102 | bool FileLoader::loadBootImage(lv_obj_t *img) 103 | { 104 | lv_image_set_src(img, FL_DRIVE_LETTER "/boot/logo.png"); 105 | return lv_image_get_src((lv_obj_t *)img) != nullptr; 106 | } 107 | -------------------------------------------------------------------------------- /source/util/ILog.cpp: -------------------------------------------------------------------------------- 1 | #ifdef USE_ILOG 2 | #include "util/ILog.h" 3 | 4 | ILog *ILog::_logger = nullptr; 5 | #endif -------------------------------------------------------------------------------- /source/util/LinuxHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "util/LinuxHelper.h" 2 | #include 3 | #include 4 | #include 5 | 6 | uint32_t LinuxHelper::getAvailableMem(void) 7 | { 8 | return getMem("MemAvailable:"); 9 | } 10 | 11 | uint32_t LinuxHelper::getFreeMem(void) 12 | { 13 | return getMem("MemFree:"); 14 | } 15 | 16 | uint32_t LinuxHelper::getTotalMem(void) 17 | { 18 | return getMem("MemTotal:"); 19 | } 20 | 21 | uint32_t LinuxHelper::getMem(const char *entry) 22 | { 23 | std::string token; 24 | std::ifstream file("/proc/meminfo"); 25 | while (file >> token) { 26 | if (token == entry) { 27 | unsigned long mem; 28 | if (file >> mem) { 29 | return mem; 30 | } else { 31 | return 0; 32 | } 33 | } 34 | // Ignore the rest of the line 35 | file.ignore(std::numeric_limits::max(), '\n'); 36 | } 37 | return 0; 38 | } -------------------------------------------------------------------------------- /source/util/SharedQueue.cpp: -------------------------------------------------------------------------------- 1 | #include "util/SharedQueue.h" 2 | 3 | SharedQueue::SharedQueue() {} 4 | 5 | SharedQueue::~SharedQueue() {} 6 | 7 | bool SharedQueue::serverSend(Packet &&p) 8 | { 9 | serverQueue.push(std::move(p)); 10 | return true; 11 | } 12 | 13 | Packet::PacketPtr SharedQueue::serverReceive() 14 | { 15 | return clientQueue.try_pop(); 16 | } 17 | 18 | size_t SharedQueue::serverQueueSize() const 19 | { 20 | return serverQueue.size(); 21 | } 22 | 23 | bool SharedQueue::clientSend(Packet &&p) 24 | { 25 | clientQueue.push(std::move(p)); 26 | return true; 27 | } 28 | 29 | Packet::PacketPtr SharedQueue::clientReceive() 30 | { 31 | return serverQueue.try_pop(); 32 | } 33 | 34 | size_t SharedQueue::clientQueueSize() const 35 | { 36 | return clientQueue.size(); 37 | } 38 | -------------------------------------------------------------------------------- /src/lib.cpp: -------------------------------------------------------------------------------- 1 | // dummy file to avoid building all sources from this lib 2 | // instead use: 3 | // 4 | // build_src_filter = ${xy_base.build_src_filter} 5 | // +<../lib/device-ui/source> 6 | // 7 | // in your platformio.ini file -------------------------------------------------------------------------------- /src/mesh-pb-constants.cpp: -------------------------------------------------------------------------------- 1 | #include "mesh-pb-constants.h" 2 | //@Ve #include "FSCommon.h" 3 | // #include "configuration.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic 10 | /// returns the encoded packet size 11 | size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct) 12 | { 13 | pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize); 14 | if (!pb_encode(&stream, fields, src_struct)) { 15 | //@Ve LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream)); 16 | assert( 17 | 0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options 18 | } else { 19 | return stream.bytes_written; 20 | } 21 | } 22 | 23 | /// helper function for decoding a record as a protobuf, we will return false if the decoding failed 24 | bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msgdesc_t *fields, void *dest_struct) 25 | { 26 | pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize); 27 | if (!pb_decode(&stream, fields, dest_struct)) { 28 | //@Ve LOG_ERROR("Can't decode protobuf reason='%s', pb_msgdesc %p\n", PB_GET_ERROR(&stream), fields); 29 | return false; 30 | } else { 31 | return true; 32 | } 33 | } 34 | 35 | #ifdef FSCom 36 | /// Read from an Arduino File 37 | bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count) 38 | { 39 | bool status = false; 40 | File *file = (File *)stream->state; 41 | 42 | if (buf == NULL) { 43 | while (count-- && file->read() != EOF) 44 | ; 45 | return count == 0; 46 | } 47 | 48 | status = (file->read(buf, count) == (int)count); 49 | 50 | if (file->available() == 0) 51 | stream->bytes_left = 0; 52 | 53 | return status; 54 | } 55 | 56 | /// Write to an arduino file 57 | bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count) 58 | { 59 | File *file = (File *)stream->state; 60 | // LOG_DEBUG("writing %d bytes to protobuf file\n", count); 61 | return file->write(buf, count) == count; 62 | } 63 | #endif 64 | 65 | bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count) 66 | { 67 | for (pb_size_t i = 0; i < count; i++) 68 | if (array[i] == n) 69 | return true; 70 | 71 | return false; 72 | } 73 | -------------------------------------------------------------------------------- /src/mesh-pb-constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "mesh/generated/meshtastic/admin.pb.h" 5 | #include "mesh/generated/meshtastic/deviceonly.pb.h" 6 | #include "mesh/generated/meshtastic/localonly.pb.h" 7 | #include "mesh/generated/meshtastic/mesh.pb.h" 8 | 9 | // this file defines constants which come from mesh.options 10 | 11 | // Tricky macro to let you find the sizeof a type member 12 | #define member_size(type, member) sizeof(((type *)0)->member) 13 | 14 | /// max number of packets which can be waiting for delivery to android - note, this value comes from mesh.options protobuf 15 | // FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in 16 | // RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0])) 17 | #ifndef MAX_RX_TOPHONE 18 | #define MAX_RX_TOPHONE 32 19 | #endif 20 | 21 | /// max number of nodes allowed in the mesh 22 | #ifndef MAX_NUM_NODES 23 | #define MAX_NUM_NODES 100 24 | #endif 25 | 26 | /// Max number of channels allowed 27 | #define MAX_NUM_CHANNELS (member_size(meshtastic_ChannelFile, channels) / member_size(meshtastic_ChannelFile, channels[0])) 28 | 29 | /// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic 30 | /// returns the encoded packet size 31 | size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct); 32 | 33 | /// helper function for decoding a record as a protobuf, we will return false if the decoding failed 34 | bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msgdesc_t *fields, void *dest_struct); 35 | 36 | /// Read from an Arduino File 37 | bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count); 38 | 39 | /// Write to an arduino file 40 | bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count); 41 | 42 | /** is_in_repeated is a macro/function that returns true if a specified word appears in a repeated protobuf array. 43 | * It relies on the following naming conventions from nanopb: 44 | * 45 | * pb_size_t ignore_incoming_count; 46 | * uint32_t ignore_incoming[3]; 47 | */ 48 | bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count); 49 | 50 | #define is_in_repeated(name, n) is_in_helper(n, name, name##_count) -------------------------------------------------------------------------------- /tests/test_GeoPoint.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/GeoPoint.h" -------------------------------------------------------------------------------- /tests/test_MapPanel.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics/map/MapPanel.h" 2 | #include 3 | 4 | class TestMapPanel : public MapPanel 5 | { 6 | public: 7 | TestMapPanel(lv_obj_t *p, ITileService *s = nullptr) : MapPanel(p, s) {} 8 | void setHome(GeoPoint &p) { home = p; } 9 | void setCurrent(GeoPoint &p) { current = p; } 10 | void setScrolled(GeoPoint &p) { scrolled = p; } 11 | void redraw(void) { MapPanel::redraw(); } 12 | void center(void) { MapPanel::center(); } 13 | void setWidthPixel(int16_t width) { widthPixel = width; } 14 | void setHeightPixel(int16_t height) { heightPixel = height; } 15 | void setXStart(uint32_t x) { xStart = x; } 16 | void setYStart(uint32_t y) { yStart = y; } 17 | void setTilesX(uint32_t x) { tilesX = x; } 18 | void setTilesY(uint32_t y) { tilesY = y; } 19 | void setXOffset(int16_t x) { xOffset = x; } 20 | void setYOffset(int16_t y) { yOffset = y; } 21 | uint32_t getXStart() const { return xStart; } 22 | uint32_t getYStart() const { return yStart; } 23 | uint8_t getTilesX() const { return tilesX; } 24 | uint8_t getTilesY() const { return tilesY; } 25 | int16_t getXOffset() const { return xOffset; } 26 | int16_t getYOffset() const { return yOffset; } 27 | 28 | void redrawAll(void) 29 | { 30 | for (int i = 0; i <= tilesX; i++) { 31 | MapPanel::redraw(); 32 | } 33 | } 34 | }; 35 | 36 | TEST_CASE("MapPanel::scroll") 37 | { 38 | TestMapPanel mapPanel(nullptr, nullptr); 39 | 40 | mapPanel.redrawAll(); 41 | mapPanel.printTiles(); 42 | 43 | SUBCASE("Scroll left by one pixel") 44 | { 45 | for (int i = 0; i < 256; i++) { 46 | CHECK(mapPanel.scroll(-1, 0, 256)); 47 | mapPanel.printTiles(); 48 | } 49 | } 50 | 51 | SUBCASE("Scroll right by one pixel") 52 | { 53 | for (int i = 0; i < 256; i++) { 54 | CHECK(mapPanel.scroll(1, 0, 256)); 55 | mapPanel.printTiles(); 56 | } 57 | } 58 | 59 | SUBCASE("Scroll up by one pixel") 60 | { 61 | for (int i = 0; i < 256; i++) { 62 | CHECK(mapPanel.scroll(0, -1, 256)); 63 | mapPanel.printTiles(); 64 | } 65 | } 66 | 67 | SUBCASE("Scroll down by one pixel") 68 | { 69 | for (int i = 0; i < 256; i++) { 70 | CHECK(mapPanel.scroll(0, 1, 256)); 71 | mapPanel.printTiles(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 2.6.2 --------------------------------------------------------------------------------