From 3ce6b065e46762899ccdfa698fb2c42a37c10a4b Mon Sep 17 00:00:00 2001 From: "Michael A. Cojocari" Date: Sun, 11 Oct 2026 15:07:29 -0400 Subject: [PATCH] Add Elecrow CrowPanel Advance 5.0 build target New env crowpanel_50_companion_radio_touch for the 800x480 ST7262 RGB panel, GT911 touch and an SX1262 in the wireless slot. Board quirks follow Camillia-MT: 14 MHz pixel clock, internal-RAM draw band, PCA9557 (V1.0) / MCU (V1.1+) backlight controller detection with GT911 reset at 0x5D or 0x14, NSS on GPIO8 (V1.2+), no SD/GPS/battery. Large-screen layouts and UI-size fonts; browser file transfer to the internal tiles partition. Added to the upload helper, release script, flasher manifests, site, DEVICES.md, README and NOTICE. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- DEVICES.md | 30 ++ NOTICE | 7 + README.md | 2 + boards/crowpanel_50.json | 45 +++ deploy/flasher/index.html | 7 + deploy/site/docs.html | 4 +- deploy/site/index.html | 6 + platformio.ini | 151 ++++++++++ scripts/build-upload-monitor.sh | 5 + scripts/build/gen-flasher-meta.py | 1 + scripts/build/gen-touch-fonts.sh | 2 +- scripts/release.sh | 2 +- src/helpers/esp32/TouchPrefsStore.cpp | 2 + src/helpers/esp32/WebFileTransferConfig.h | 8 +- src/helpers/input/LgfxGt911Touch.cpp | 19 +- src/main.cpp | 2 +- src/ui-touch/UITask.cpp | 50 ++-- src/ui-touch/device_caps.h | 13 + src/ui-touch/ui_font_18.c | 4 +- src/ui-touch/ui_font_20.c | 4 +- src/ui-touch/ui_font_24.c | 4 +- variants/crowpanel_50/CrowPanel50Board.h | 18 ++ variants/crowpanel_50/CrowPanel50Display.cpp | 294 +++++++++++++++++++ variants/crowpanel_50/CrowPanel50Display.h | 82 ++++++ variants/crowpanel_50/target.cpp | 35 +++ variants/crowpanel_50/target.h | 23 ++ 26 files changed, 787 insertions(+), 33 deletions(-) create mode 100644 boards/crowpanel_50.json create mode 100644 variants/crowpanel_50/CrowPanel50Board.h create mode 100644 variants/crowpanel_50/CrowPanel50Display.cpp create mode 100644 variants/crowpanel_50/CrowPanel50Display.h create mode 100644 variants/crowpanel_50/target.cpp create mode 100644 variants/crowpanel_50/target.h diff --git a/DEVICES.md b/DEVICES.md index 30d5b2c..e51b90d 100644 --- a/DEVICES.md +++ b/DEVICES.md @@ -15,6 +15,7 @@ USB) and the [GitHub releases](https://github.com/ALLFATHER-BV/wadamesh/releases | Tanmatsu | ESP32-P4 + ESP32-C6, SX1262 | 4" 800x480, 69-key keyboard (no touch) | Tanmatsu app store on the device (runs under the badge.team launcher, not web-flashable) | Store tracks the test channel | Fully supported; LoRa + Wi-Fi + Bluetooth simultaneously, standalone and companion in one | | Elecrow ThinkNode M9 | ESP32-S3, LR1110 | 2.4" 240x320 (no touch), I2C QWERTY + d-pad | Web flasher | Beta (new in beta_38) | Hardware-complete community port by ded (#138): GPS, microSD, buzzer, lock screen, d-pad navigation | | Elecrow CrowPanel Advance 3.5 | ESP32-S3, external SX1262 | 3.5" ILI9488 480x320 touch (GT911) | Web flasher (beta) | Beta, hardware validation pending | Requires an SX1262 module in the LoRa expansion slot; not for other CrowPanel models | +| Elecrow CrowPanel Advance 5.0 | ESP32-S3, external SX1262 | 5" ST7262 RGB 800x480 touch (GT911) | Web flasher (beta) | Beta, hardware validation pending | V1.1+ boards; SX1262 in the wireless slot with the function DIP switch on the wireless module; no SD, GPS or battery gauge | | RAK WisMesh Tap V2 (RAK3312) | ESP32-S3, SX1262 | Touch display (LovyanGFX, 30+ fps) | Web flasher | Beta (new in beta_38) | Early community port by Ethac.chen (#136); core mesh, chat and map working | | LilyGo T-Lora Pager | ESP32-S3, LR1121 or SX1262 | 2.33" IPS LCD, 480x222 (no touch), QWERTY + rotary encoder | Web flasher (pick the build matching your radio) | Beta (new in beta_45) | Fully supported; keyboard-first navigation, microSD, map tile packs | | Heltec V4-R8 + Expansion Kit V2 | ESP32-S3 (8 MB octal PSRAM), SX1262 | 2.4" touch (CHSC6x) | Web flasher | Beta (new in beta_45) | As the V4 plus microSD and the Expansion Kit sensors; buzzer supported | @@ -124,6 +125,35 @@ USB) and the [GitHub releases](https://github.com/ALLFATHER-BV/wadamesh/releases CrowPanel. Like Wio Tracker L2, it is included in the all-target builds and local beta release scripts, not the two-board stable CI workflow. Select it in the upload helper with `scripts/build-upload-monitor.sh --crowpanel-35`. +- **CrowPanel Advance 5.0**: `crowpanel_50_companion_radio_touch` is a + 16 MB flash / 8 MB OPI PSRAM build for the 800x480 ST7262 RGB panel, the + GT911 touch controller and an SX1262 in the wireless-module slot (J8/J9). The + board quirks follow Camillia-MT's port: + - The panel is driven over the 16-bit RGB bus from a PSRAM framebuffer at a + 14 MHz pixel clock (Elecrow's demo uses 21 MHz); the slower clock leaves + PSRAM bandwidth for WiFi and LVGL so the picture does not drift or tear. + LVGL's draw band stays in internal RAM for the same reason. + - Backlight and touch reset sit behind an I2C controller on GPIO15/16 that + changed between revisions: a PCA9557 at 0x18 on V1.0, a board MCU at 0x30 + from V1.1. The driver detects which one answers, resets the GT911 through + it and accepts the touch controller at either 0x5D or 0x14. Below full + brightness the MCU dims by PWM, which can whine; 100% is steady. + - The function DIP switch on the back routes GPIO 2/4/5/6/19/20 to the + wireless slot, the microSD slot or the speaker/microphone. Set it to the + wireless module (S1=0, S0=1), or the radio is not found (the serial log + says so). The microSD slot is therefore unusable; browser Transfer uses the + internal tiles partition instead, as on the Attaky. + - The radio's chip select is GPIO8 on V1.2/V1.3 boards (current production) + and GPIO0 on V1.0. The build targets GPIO8; a V1.0 board is detected at boot + and logged, and needs `P_LORA_NSS=0`. + - No GPS (no free GPIO pair for a UART; 17/18 are panel data), no battery + voltage sensing and no free user button, so hard lock is disabled and the + screen wakes by touch. + It starts in native 800x480 landscape; Display settings can switch to + 480x800 portrait. It uses the large-screen layouts and UI-size presets of the + Tanmatsu. Serial uses the board's CH340 USB-to-UART bridge. Display, touch, + backlight and radio still need validation on a physical unit. Select it in + the upload helper with `scripts/build-upload-monitor.sh --crowpanel-50`. ## Requested boards diff --git a/NOTICE b/NOTICE index a62b524..5713fb3 100644 --- a/NOTICE +++ b/NOTICE @@ -97,5 +97,12 @@ Camillia-MT (CrowPanel Advance 3.5 software-SPI microSD backend) Adapted from src/sd_soft_spi.cpp; integrated with WadaMesh's filesystem API and concurrent storage consumers. +Camillia-MT (CrowPanel Advance 5.0 display, backlight and touch bring-up) + Copyright (c) 2025-2026 Michael A. Cojocari and contributors + GNU General Public License v3.0 or later + The RGB panel configuration, revision detection, backlight controller and + GT911 reset in variants/crowpanel_50/CrowPanel50Display.cpp are adapted + from src/hal/crowpanel_50_display.h and src/hal/hw_crowpanel_50.h. + This list is not exhaustive and is expanded as dependencies are vendored or added. diff --git a/README.md b/README.md index b67f84d..e96baaa 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ per-board status. - LilyGo T-Display P4: built from `tdisplay_p4/` (ESP-IDF); AMOLED + SX1262 by default, with `WADA_P4_LCD=1` for TFT-LCD and `WADA_P4_LR2021=1` for LR2021 (beta) - Attaky Mesh Series: env `attaky_mesh_series_companion_radio_touch` (beta) - Elecrow CrowPanel Advance 3.5: env `crowpanel_35_companion_radio_touch` (beta, hardware validation pending; requires an SX1262 in the expansion slot) +- Elecrow CrowPanel Advance 5.0: env `crowpanel_50_companion_radio_touch` (beta, hardware validation pending; requires an SX1262 in the wireless slot and the DIP switch set to it) ## Architecture @@ -100,6 +101,7 @@ automatically: pio run -e heltec_v4_tft_companion_radio_usb_tcp_touch # Heltec V4 TFT pio run -e LilyGo_TDeck_companion_radio_touch # LilyGo T-Deck pio run -e crowpanel_35_companion_radio_touch # CrowPanel Advance 3.5 +pio run -e crowpanel_50_companion_radio_touch # CrowPanel Advance 5.0 # or just `pio run` to build the configured default environments ``` diff --git a/boards/crowpanel_50.json b/boards/crowpanel_50.json new file mode 100644 index 0000000..fcec29b --- /dev/null +++ b/boards/crowpanel_50.json @@ -0,0 +1,45 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_16MB.csv", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=0", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "psram_type": "opi", + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Elecrow CrowPanel Advance 5.0 (N16R8)", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": false, + "wait_for_upload_port": false, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.elecrow.com/", + "vendor": "Elecrow" +} diff --git a/deploy/flasher/index.html b/deploy/flasher/index.html index c38dbbf..87abd6c 100644 --- a/deploy/flasher/index.html +++ b/deploy/flasher/index.html @@ -111,6 +111,13 @@ Use Chrome or Edge on desktop (Web Serial). +
+

Elecrow CrowPanel Advance 5.0 Beta

+

5" 800×480 touch display and an SX1262 module in the wireless slot.

+

For the Advance 5.0 (V1.1 or later) only. Hardware validation is pending; set the function DIP switch to the wireless module and attach the SX1262.

+ + Use Chrome or Edge on desktop (Web Serial). +
How it works: plug the device in over USB, click Connect, pick the serial port, diff --git a/deploy/site/docs.html b/deploy/site/docs.html index 0eadfae..787f082 100644 --- a/deploy/site/docs.html +++ b/deploy/site/docs.html @@ -349,7 +349,7 @@
  • Wrong tries slow down: every fifth miss makes you wait, 30 seconds at first and doubling up to 4 minutes, and the count survives a reboot.
  • Change PIN asks for the current one first, and so does turning PIN lock off.
  • -

    The Heltec V4, Heltec V4-R8, Attaky and CrowPanel have no lock screen, so no PIN either.

    +

    The Heltec V4, Heltec V4-R8, Attaky and CrowPanels have no lock screen, so no PIN either.

    The lock screen
    The lock screen
    @@ -493,7 +493,7 @@
  • Aurora: one still sky, drawn once.
  • Moving aurora: the same sky drifting in a slow, seamless loop. It rests while you use the device and while the screen is off, locked or showing the screensaver.
  • Colour: one plain colour, your accent unless you pick another, kept dark enough by night and light enough by day for text to read.
  • -
  • Picture: a JPEG from the file viewer (Set as background), cut to fill the screen and dimmed by night. Not on the Heltec V4 and V4-R8, the CrowPanel or the Attaky.
  • +
  • Picture: a JPEG from the file viewer (Set as background), cut to fill the screen and dimmed by night. Not on the Heltec V4 and V4-R8, the CrowPanels or the Attaky.
  • None: the plain theme, black by night and light grey by day.
  • The background waits for Apply and restart like the colours. High contrast has no background, and the e-paper T-Deck Pro and Max show none.

    diff --git a/deploy/site/index.html b/deploy/site/index.html index 0177957..124cef9 100644 --- a/deploy/site/index.html +++ b/deploy/site/index.html @@ -599,6 +599,7 @@ LilyGoT-Deck MaxE-paper keyboard and touch handheld.ExperimentalBeta HeltecV4-R8V4 with 8 MB PSRAM and an SD card.ExperimentalBeta ElecrowCrowPanel Advance 3.53.5-inch touch panel with an SX1262 module.ExperimentalBeta + ElecrowCrowPanel Advance 5.05-inch 800×480 touch panel with an SX1262 module.ExperimentalBeta @@ -757,6 +758,11 @@ Download .bin

    Beta only. Attach the SX1262 before booting; flash with care and report what works.

    +

    CrowPanel Advance 5.0 (experimental)

    5" 800×480 touch display; requires an SX1262 module in the wireless slot and the function DIP switch set to the wireless module.

    + Partially supportedHardware validation pendingThis build is for the Advance 5.0 (V1.1 and later) with an SX1262 in the wireless slot, not other CrowPanel models. Display, touch, backlight and radio follow the hardware reference; behaviour on a physical unit has not been verified. The DIP switch hands the radio's pins to the microSD slot otherwise, so there is no SD card; there is also no GPS and no battery monitoring. Landscape is the default, with portrait available in Display settings. + + Download .bin +

    Beta only. Set the DIP switch and attach the SX1262 before booting; flash with care and report what works.

    diff --git a/platformio.ini b/platformio.ini index 4d505a0..47eed4b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1464,6 +1464,157 @@ lib_deps = densaugeo/base64 @ ~1.4.0 knolleary/PubSubClient @ ^2.8.0 +; Elecrow CrowPanel Advance 5.0 — ESP32-S3, 800x480 ST7262 RGB panel, GT911 and +; SX1262 in the wireless slot. Board quirks follow Camillia-MT; see DEVICES.md. +; ====================================================================== +[env:crowpanel_50_companion_radio_touch] +platform = platformio/espressif32@6.11.0 +framework = arduino +board = crowpanel_50 +monitor_speed = 115200 +upload_speed = 921600 +board_build.arduino.memory_type = qio_opi +board_build.flash_size = 16MB +board_upload.flash_size = 16MB +board_upload.maximum_size = 16777216 +board_build.partitions = partitions_tft_touch.csv +extra_scripts = pre:scripts/build/pre_gen_baked.py, pre:scripts/inject_wifi_env.py, pre:scripts/build/patch_lvgl_anim_uaf.py, pre:scripts/build/patch_lvgl_arabic.py, pre:scripts/build/patch_lgfx_dmadesc.py, pre:scripts/build/perf_lvgl.py, merge-bin.py +custom_lvgl_o2 = yes ; LVGL at -O2 (scripts/build/perf_lvgl.py), like the T-Deck +custom_lvgl_iram = lv_draw_sw_blend.c, lv_draw_label.c ; their hot loops in IRAM + +build_unflags = + -std=gnu++11 + +build_flags = + -I"${platformio.libdeps_dir}/${this.__env__}/MicroNMEA/src" + -I"${platformio.libdeps_dir}/${this.__env__}/LovyanGFX/src" + -I variants/crowpanel_50 + -I include + -I src + -I src/ui-touch + -Wno-deprecated-declarations + -Wno-unused-parameter + -std=gnu++17 + -DNDEBUG + -DRADIOLIB_STATIC_ONLY=1 + -DRADIOLIB_GODMODE=1 + -D MC_VENDORED_TOUCH_APP + -D ESP32_PLATFORM + -D HAS_CROWPANEL_50=1 + -D BOARD_HAS_PSRAM=1 + -D ESP32_CPU_FREQ=240 + -D CORE_DEBUG_LEVEL=0 + -D LORA_FREQ=869.618 + -D LORA_BW=62.5 + -D LORA_SF=8 + -D USE_SX1262 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + ; SX1262 in the J8/J9 wireless slot. The function DIP switch on the back must + ; select the wireless module (S1=0, S0=1); its other positions hand these pins + ; to the SD card or the speaker/microphone. CS is GPIO8 on V1.2/V1.3 boards + ; (current production); V1.0 boards wire it to GPIO0 and need P_LORA_NSS=0. + -D P_LORA_NSS=8 + -D P_LORA_RESET=19 + -D P_LORA_DIO_1=20 + -D P_LORA_BUSY=2 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=4 + -D P_LORA_MOSI=6 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=3.3 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D RADIOLIB_EXCLUDE_CC1101=1 + -D RADIOLIB_EXCLUDE_RF69=1 + -D RADIOLIB_EXCLUDE_SX1231=1 + -D RADIOLIB_EXCLUDE_SI443X=1 + -D RADIOLIB_EXCLUDE_RFM2X=1 + -D RADIOLIB_EXCLUDE_SX128X=1 + -D RADIOLIB_EXCLUDE_AFSK=1 + -D RADIOLIB_EXCLUDE_AX25=1 + -D RADIOLIB_EXCLUDE_HELLSCHREIBER=1 + -D RADIOLIB_EXCLUDE_MORSE=1 + -D RADIOLIB_EXCLUDE_APRS=1 + -D RADIOLIB_EXCLUDE_BELL=1 + -D RADIOLIB_EXCLUDE_RTTY=1 + -D RADIOLIB_EXCLUDE_SSTV=1 + -D ENV_INCLUDE_GPS=0 + -D ENV_INCLUDE_AHTX0=0 + -D ENV_INCLUDE_BME280=0 + -D ENV_INCLUDE_BMP280=0 + -D ENV_INCLUDE_SHTC3=0 + -D ENV_INCLUDE_SHT4X=0 + -D ENV_INCLUDE_LPS22HB=0 + -D ENV_INCLUDE_INA3221=0 + -D ENV_INCLUDE_INA219=0 + -D ENV_INCLUDE_INA226=0 + -D ENV_INCLUDE_INA260=0 + -D ENV_INCLUDE_MLX90614=0 + -D ENV_INCLUDE_VL53L0X=0 + -D ENV_INCLUDE_BME680=0 + -D ENV_INCLUDE_BMP085=0 + -D PIN_BOARD_SDA=15 + -D PIN_BOARD_SCL=16 + ; No GPS, SD card, battery gauge or spare button: the 16-bit RGB panel takes + ; most GPIOs and the DIP switch gives the rest to the radio. + -D DISPLAY_CLASS=CrowPanel50Display + -D DISPLAY_ROTATION=3 + -D DISPLAY_SCALE_X=1.0 + -D DISPLAY_SCALE_Y=1.0 + -D UI_LVGL=1 + -D HAS_TOUCH_UI=1 + -D FIRMWARE_OTA_ENV='"crowpanel_50_companion_radio_touch"' + -D ENABLE_ADVERT_ON_BOOT=0 + -D LV_CONF_PATH=lv_conf.h + -D LV_CONF_INCLUDE_SIMPLE=1 + -D LV_MEMCPY_MEMSET_STD=1 ; newlib memcpy/memset in LVGL (see include/lv_conf.h) + -D MAX_CONTACTS=2000 + -D MAX_GROUP_CHANNELS=40 + -D ADMIN_PASSWORD='"password"' + -D MULTI_TRANSPORT_COMPANION=1 + -D WADAMESH_FORK_BUILD=1 + -D TCP_PORT=5000 + -D WS_PORT=8765 + -D BLE_PIN_CODE=123456 + -D ENABLE_PRIVATE_KEY_IMPORT=1 + -D ENABLE_PRIVATE_KEY_EXPORT=1 + -D CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=1 + -D CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 + -D CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 + +build_src_filter = + +<*.cpp> + +<*.c> + + + + + + + - + + + + + + + +<../variants/crowpanel_50/*.cpp> + +lib_deps = + https://github.com/ALLFATHER-BV/meshcomod.git#core-v1.17.4 + SPI + Wire + SD + jgromes/RadioLib @ ^7.6.0 + rweather/Crypto @ ^0.4.0 + adafruit/RTClib @ ^2.1.3 + melopero/Melopero RV3028 @ ^1.1.0 + electroniccats/CayenneLPP @ 1.6.1 + h2zero/NimBLE-Arduino @ 1.4.3 + stevemarple/MicroNMEA @ ^2.0.6 + ESP32Async/ESPAsyncWebServer @ 3.10.3 + file://arch/esp32/AsyncElegantOTA + lvgl/lvgl @ ^8.3.11 + lovyan03/LovyanGFX @ 1.2.27 + densaugeo/base64 @ ~1.4.0 + knolleary/PubSubClient @ ^2.8.0 + [env:tlora_pager_lr1121_companion_radio_touch] platform = platformio/espressif32@6.11.0 framework = arduino diff --git a/scripts/build-upload-monitor.sh b/scripts/build-upload-monitor.sh index 69a01c6..e753fc9 100755 --- a/scripts/build-upload-monitor.sh +++ b/scripts/build-upload-monitor.sh @@ -15,6 +15,7 @@ HELTEC_R8_ENV="heltec_v4_r8_tft_companion_radio_usb_tcp_touch" ATTAKY_ENV="attaky_mesh_series_companion_radio_touch" WIO_ENV="wio_tracker_l2_companion_radio_touch" CROWPANEL_ENV="crowpanel_35_companion_radio_touch" +CROWPANEL_50_ENV="crowpanel_50_companion_radio_touch" PAGER_LR1121_ENV="tlora_pager_lr1121_companion_radio_touch" PAGER_SX1262_ENV="tlora_pager_sx1262_companion_radio_touch" M9_ENV="ThinkNode_M9_companion_radio_touch" @@ -94,6 +95,7 @@ env_label() { "$ATTAKY_ENV") echo "Attaky Mesh Series" ;; "$WIO_ENV") echo "Seeed Wio Tracker L2" ;; "$CROWPANEL_ENV") echo "Elecrow CrowPanel Advance 3.5" ;; + "$CROWPANEL_50_ENV") echo "Elecrow CrowPanel Advance 5.0" ;; "$PAGER_LR1121_ENV") echo "LilyGo T-LoRa Pager LR1121" ;; "$PAGER_SX1262_ENV") echo "LilyGo T-LoRa Pager SX1262" ;; "$M9_ENV") echo "ThinkNode M9" ;; @@ -120,6 +122,7 @@ Devices: --attaky Attaky Mesh Series --wio Seeed Wio Tracker L2 --crowpanel-35 Elecrow CrowPanel Advance 3.5 + --crowpanel-50 Elecrow CrowPanel Advance 5.0 --pager-lr1121 LilyGo T-LoRa Pager LR1121 --pager-sx1262 LilyGo T-LoRa Pager SX1262 --m9 ThinkNode M9 @@ -171,6 +174,7 @@ prompt_for_device() { "$ATTAKY_ENV" "$WIO_ENV" "$CROWPANEL_ENV" + "$CROWPANEL_50_ENV" "$PAGER_LR1121_ENV" "$PAGER_SX1262_ENV" "$RAK_ENV" @@ -250,6 +254,7 @@ while [ $# -gt 0 ]; do --attaky) select_env "$ATTAKY_ENV" ;; --wio) select_env "$WIO_ENV" ;; --crowpanel-35) select_env "$CROWPANEL_ENV" ;; + --crowpanel-50) select_env "$CROWPANEL_50_ENV" ;; --pager-lr1121) select_env "$PAGER_LR1121_ENV" ;; --pager-sx1262) select_env "$PAGER_SX1262_ENV" ;; --m9) select_env "$M9_ENV" ;; diff --git a/scripts/build/gen-flasher-meta.py b/scripts/build/gen-flasher-meta.py index 51b9bbf..f7c14aa 100644 --- a/scripts/build/gen-flasher-meta.py +++ b/scripts/build/gen-flasher-meta.py @@ -45,6 +45,7 @@ BOARDS = { "manifest-attaky.json": ("wadamesh - Attaky Core (experimental)", "wadamesh-attaky-merged.bin", "ESP32-S3"), "manifest-wio-tracker-l2.json": ("wadamesh - Seeed Wio Tracker L2 (experimental)", "wadamesh-wio-tracker-l2-merged.bin", "ESP32-S3"), "manifest-crowpanel-35.json": ("wadamesh - Elecrow CrowPanel Advance 3.5 (experimental)", "wadamesh-crowpanel-35-merged.bin", "ESP32-S3"), + "manifest-crowpanel-50.json": ("wadamesh - Elecrow CrowPanel Advance 5.0 (experimental)", "wadamesh-crowpanel-50-merged.bin", "ESP32-S3"), "manifest-tdeck-pro-v1-0.json": ("wadamesh - LilyGo T-Deck Pro V1.0 (experimental)", "wadamesh-tdeck-pro-v1-0-merged.bin", "ESP32-S3"), "manifest-tdeck-pro-v1-1.json": ("wadamesh - LilyGo T-Deck Pro V1.1 (experimental)", "wadamesh-tdeck-pro-v1-1-merged.bin", "ESP32-S3"), "manifest-tdeck-max.json": ("wadamesh - LilyGo T-Deck Max (experimental)", "wadamesh-tdeck-max-merged.bin", "ESP32-S3"), diff --git a/scripts/build/gen-touch-fonts.sh b/scripts/build/gen-touch-fonts.sh index b05a3bd..0504828 100755 --- a/scripts/build/gen-touch-fonts.sh +++ b/scripts/build/gen-touch-fonts.sh @@ -414,7 +414,7 @@ generate_fallback_icons() { # some translation units and not others. 18/20/24 are the bigger UI-size presets # (every board with a text-size setting; the V4 has none, its flash is full). # DOC_CAPTURE builds carry them all: they render other boards' sizes and scales for review. -ui_big="defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE)" +ui_big="defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE)" for size in 12 14 16 28; do generate_ui "$size" done diff --git a/scripts/release.sh b/scripts/release.sh index 7341066..4ec4bf1 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -29,7 +29,7 @@ DEST="${WADAMESH_VPS:-}"; DEST_PATH="${WADAMESH_VPS_PATH:-/srv/wadamesh/firmware # env:binname pairs — plain string form (works on macOS's bash 3.2; no associative arrays). # All S3/PIO boards. The T-Display P4 is an ESP32-P4 IDF build (tdisplay_p4/) handled OUT of band # — see the release skill / the P4 build+merge step — because this loop is PlatformIO-only. -ENVS="heltec_v4_tft_companion_radio_usb_tcp_touch:wadamesh-heltec-v4-tft LilyGo_TDeck_companion_radio_touch:wadamesh-tdeck ThinkNode_M9_companion_radio_touch:wadamesh-thinknode-m9 rak_tap_v2_companion_radio_touch:wadamesh-rak-tap-v2 heltec_v4_r8_tft_companion_radio_usb_tcp_touch:wadamesh-heltec-v4-r8-tft tlora_pager_lr1121_companion_radio_touch:wadamesh-tlora-pager-lr1121 tlora_pager_sx1262_companion_radio_touch:wadamesh-tlora-pager-sx1262 attaky_mesh_series_companion_radio_touch:wadamesh-attaky wio_tracker_l2_companion_radio_touch:wadamesh-wio-tracker-l2 crowpanel_35_companion_radio_touch:wadamesh-crowpanel-35 LilyGo_TDeck_Pro_v1_0_companion_radio_touch:wadamesh-tdeck-pro-v1-0 LilyGo_TDeck_Pro_v1_1_companion_radio_touch:wadamesh-tdeck-pro-v1-1 LilyGo_TDeck_Max_companion_radio_touch:wadamesh-tdeck-max" +ENVS="heltec_v4_tft_companion_radio_usb_tcp_touch:wadamesh-heltec-v4-tft LilyGo_TDeck_companion_radio_touch:wadamesh-tdeck ThinkNode_M9_companion_radio_touch:wadamesh-thinknode-m9 rak_tap_v2_companion_radio_touch:wadamesh-rak-tap-v2 heltec_v4_r8_tft_companion_radio_usb_tcp_touch:wadamesh-heltec-v4-r8-tft tlora_pager_lr1121_companion_radio_touch:wadamesh-tlora-pager-lr1121 tlora_pager_sx1262_companion_radio_touch:wadamesh-tlora-pager-sx1262 attaky_mesh_series_companion_radio_touch:wadamesh-attaky wio_tracker_l2_companion_radio_touch:wadamesh-wio-tracker-l2 crowpanel_35_companion_radio_touch:wadamesh-crowpanel-35 crowpanel_50_companion_radio_touch:wadamesh-crowpanel-50 LilyGo_TDeck_Pro_v1_0_companion_radio_touch:wadamesh-tdeck-pro-v1-0 LilyGo_TDeck_Pro_v1_1_companion_radio_touch:wadamesh-tdeck-pro-v1-1 LilyGo_TDeck_Max_companion_radio_touch:wadamesh-tdeck-max" # Per-channel destination paths. if [ "$MODE" = "stable" ]; then diff --git a/src/helpers/esp32/TouchPrefsStore.cpp b/src/helpers/esp32/TouchPrefsStore.cpp index f17cf69..3980b7e 100644 --- a/src/helpers/esp32/TouchPrefsStore.cpp +++ b/src/helpers/esp32/TouchPrefsStore.cpp @@ -61,6 +61,8 @@ static const uint8_t DEFAULT_SIG_PROBE_EN = 1; // signal discover static const uint16_t DEFAULT_SIG_POLL_MIN = 5; // minutes between probes #if defined(HAS_CROWPANEL_35) static const uint8_t DEFAULT_UI_ROTATION = 3; // upright ILI9488 landscape +#elif defined(HAS_CROWPANEL_50) +static const uint8_t DEFAULT_UI_ROTATION = 3; // native 800x480 landscape #elif defined(ATTAKY_MESH_SERIES) static const uint8_t DEFAULT_UI_ROTATION = 1; // landscape (LV_DISP_ROT_90), as before it was a setting #else diff --git a/src/helpers/esp32/WebFileTransferConfig.h b/src/helpers/esp32/WebFileTransferConfig.h index afd9829..8831f84 100644 --- a/src/helpers/esp32/WebFileTransferConfig.h +++ b/src/helpers/esp32/WebFileTransferConfig.h @@ -3,14 +3,14 @@ // Browser file transfer requires the shared WebSocket transport and a removable // card backend. Keep internal SPIFFS/LittleFS-only boards out: /transfer can hold // large uploads and is intended to remain user-removable storage. The exception -// is the Attaky Mesh Deck, which has no card slot at all: it transfers to its -// internal LittleFS "tiles" partition (never SPIFFS, which holds the settings, +// is the Attaky Mesh Deck and the CrowPanel Advance 5.0, which have no usable card +// slot: they transfer to the internal LittleFS "tiles" partition (never SPIFFS, which holds the settings, // contacts and chats), within that partition's free space. #if defined(MULTI_TRANSPORT_COMPANION) && \ (defined(HAS_TDECK_GT911) || defined(HAS_TDECK_PRO) || defined(TLORA_PAGER) || \ defined(HAS_THINKNODE_M9) || defined(HELTEC_LORA_V4_R8) || \ defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_TDISPLAY_P4) || \ - defined(HAS_TANMATSU) || defined(ATTAKY_MESH_SERIES)) + defined(HAS_TANMATSU) || defined(ATTAKY_MESH_SERIES) || defined(HAS_CROWPANEL_50)) #define WADA_WEB_FILE_TRANSFER 1 #else #define WADA_WEB_FILE_TRANSFER 0 @@ -24,7 +24,7 @@ #define WADA_WEB_FILE_TRANSFER_SDMMC 0 #endif -#if WADA_WEB_FILE_TRANSFER && defined(ATTAKY_MESH_SERIES) +#if WADA_WEB_FILE_TRANSFER && (defined(ATTAKY_MESH_SERIES) || defined(HAS_CROWPANEL_50)) #define WADA_WEB_FILE_TRANSFER_INTERNAL 1 #else #define WADA_WEB_FILE_TRANSFER_INTERNAL 0 diff --git a/src/helpers/input/LgfxGt911Touch.cpp b/src/helpers/input/LgfxGt911Touch.cpp index 0b9081d..0676d4d 100644 --- a/src/helpers/input/LgfxGt911Touch.cpp +++ b/src/helpers/input/LgfxGt911Touch.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#if (defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35)) && defined(ESP32) +#if (defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50)) && defined(ESP32) #include #include @@ -11,6 +11,9 @@ #if defined(HAS_CROWPANEL_35) #include extern CrowPanel35Display display; +#elif defined(HAS_CROWPANEL_50) + #include + extern CrowPanel50Display display; #else #include extern WioTrackerL2Display display; @@ -69,6 +72,16 @@ bool heltecV4CapTouchBegin() { nextProbeMs = now + 1000; Wire.beginTransmission(0x5D); if (Wire.endTransmission() != 0) return false; +#elif defined(HAS_CROWPANEL_50) + // The display reset the GT911 and recorded where it answered (0x5D or 0x14). + static uint32_t nextProbeMs = 0; + const uint32_t now = millis(); + if (nextProbeMs && (int32_t)(now - nextProbeMs) < 0) return false; + nextProbeMs = now + 1000; + const uint8_t addr = display.touchAddr(); + if (!addr) return false; + Wire.beginTransmission(addr); + if (Wire.endTransmission() != 0) return false; #endif s_ready = true; return true; @@ -90,6 +103,8 @@ bool heltecV4CapTouchStartBackgroundPoll(uint32_t periodMs) { s_periodMs = periodMs < 4 ? 4 : (periodMs > 100 ? 100 : periodMs); #if defined(HAS_CROWPANEL_35) const char* name = "crow_touch"; +#elif defined(HAS_CROWPANEL_50) + const char* name = "crow50_touch"; #else const char* name = "wio_l2_touch"; #endif @@ -104,6 +119,8 @@ void heltecV4CapTouchSetSlowPoll(bool slow) { s_periodMs = slow ? 50 : 8; } const char* heltecV4CapTouchDebug() { #if defined(HAS_CROWPANEL_35) return s_ready ? "CrowPanel Advance 3.5 GT911" : "CrowPanel GT911 not responding at 0x5D"; +#elif defined(HAS_CROWPANEL_50) + return s_ready ? "CrowPanel Advance 5.0 GT911" : "CrowPanel 5.0 GT911 not responding at 0x5D/0x14"; #else return "Wio Tracker L2 GT911"; #endif diff --git a/src/main.cpp b/src/main.cpp index 0ca7729..680c8b8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -874,7 +874,7 @@ void setup() { #endif if (r == 1) display.setDisplayRotation(1); else if (r == 3) display.setDisplayRotation(3); -#if defined(HAS_CROWPANEL_35) +#if defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) else display.setDisplayRotation(0); // restore saved portrait #endif } diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index 71f81d7..724034a 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -196,6 +196,8 @@ static_assert(ChannelSenderSplit::kMaxWireName >= (size_t)UITask::MAX_SENDER_NAM #include #elif defined(HAS_CROWPANEL_35) #include + #elif defined(HAS_CROWPANEL_50) + #include #elif defined(HAS_RAK_TAP_V2) #include // LovyanGFX FSPI on RAK Tap V2 #elif defined(HAS_TDISPLAY_P4) @@ -264,6 +266,8 @@ static_assert(ChannelSenderSplit::kMaxWireName >= (size_t)UITask::MAX_SENDER_NAM extern WioTrackerL2Display display; #elif defined(HAS_CROWPANEL_35) extern CrowPanel35Display display; + #elif defined(HAS_CROWPANEL_50) + extern CrowPanel50Display display; #elif defined(HAS_RAK_TAP_V2) || defined(HELTEC_LORA_V4_R8) extern LGFXDisplay display; #elif defined(HAS_TDISPLAY_P4) @@ -5102,7 +5106,7 @@ static void lvglFlushImpl(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_col static void applyHardwarePanelRotation(uint8_t lvgl_rot) { if (lvgl_rot == LV_DISP_ROT_90) display.setDisplayRotation(1); else if (lvgl_rot == LV_DISP_ROT_270) display.setDisplayRotation(3); -#if defined(HAS_CROWPANEL_35) +#if defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) else display.setDisplayRotation(0); #elif defined(ATTAKY_MESH_SERIES) else display.setDisplayRotation(DISPLAY_ROTATION); @@ -9029,7 +9033,7 @@ static void kbShowRotateArrows(bool show) { // control. The arrows are not created on these boards; every other reference to // the two pointers is null-guarded, so leaving them null is enough to remove // the buttons everywhere — the same pattern the retired s_kb_alt_btn uses. -#if !defined(HAS_WIO_TRACKER_L2) && !defined(HAS_CROWPANEL_35) +#if !defined(HAS_WIO_TRACKER_L2) && !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) static void kbRotLeftCb(lv_event_t* e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) return; // Tap the left arrow: rotate landscape that way; tap again returns to portrait. @@ -16263,7 +16267,7 @@ static void rotateScreenCycleCb(lv_event_t* e) { #if defined(ESP32) // Simple toggle: portrait <-> the board's upright landscape orientation. const uint8_t cur = touchPrefsGetUiRotation(); -#if defined(HAS_CROWPANEL_35) +#if defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) const uint8_t next = (cur == LV_DISP_ROT_NONE) ? (uint8_t)LV_DISP_ROT_270 : (uint8_t)LV_DISP_ROT_NONE; #else @@ -17472,7 +17476,7 @@ static void buildLockPage() { }); #endif } -#if !defined(HAS_CROWPANEL_35) // no unlock control there, so no lock to put a PIN on +#if !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) // no unlock control there, so no lock to put a PIN on { // PIN lock: off until switched on here. On asks for a new PIN twice; off and Change // ask for the current one first. The switch follows the PIN, not the tap. @@ -31574,7 +31578,7 @@ static volatile uint16_t s_tiles_free_kb = 0xFFFF; static fs::LittleFSFS s_tiles_fs; static bool s_tiles_fs_ready = false; #if WADA_WEB_FILE_TRANSFER_INTERNAL -// File Transfer's storage on the Attaky (declared with the transfer app above). +// File Transfer's storage on the Attaky and CrowPanel 5.0 (declared with the transfer app above). static fs::FS& internalTransferFs() { return s_tiles_fs; } static bool internalTransferFsReady() { return s_tiles_fs_ready; } static size_t internalTransferFsUsed() { return s_tiles_fs.usedBytes(); } @@ -32882,6 +32886,8 @@ static int wifiScanWatchdogSafe(uint32_t cap_ms, uint16_t per_chan_ms = 300) { #define WADA_BOARD_ID "wio-tracker-l2" #elif defined(HAS_CROWPANEL_35) #define WADA_BOARD_ID "crowpanel-35" +#elif defined(HAS_CROWPANEL_50) +#define WADA_BOARD_ID "crowpanel-50" #elif defined(HELTEC_LORA_V4_TFT) #define WADA_BOARD_ID "heltec-v4-tft" #else @@ -50527,7 +50533,7 @@ static void applyVolume(uint8_t pct) { if (pct > 100) pct = 100; s_volume_pct = pct; // play paths read the persisted pref; this keeps the slider live } -#elif defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) +#elif defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) #define HAS_CC_BRIGHTNESS 1 static uint8_t s_brightness_pct = 63; static void applyBrightness(uint8_t pct) { @@ -56223,7 +56229,7 @@ static lv_obj_t* hwIcon(lv_obj_t* parent, const char* glyph, uint32_t col, bool static const lv_font_t* hwFitFont(const char* txt, int w, int h) { const lv_font_t* faces[] = { &lv_font_montserrat_28, -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) &lv_font_montserrat_24, &lv_font_montserrat_20, #endif &g_font_semi_16, &g_font_semi_14, &g_font_12, @@ -64258,7 +64264,7 @@ static void buildUiTree() { }; // Both buttons use the rotation/refresh glyph; left vs. right position // tells the user which way the display will turn. -#if !defined(HAS_WIO_TRACKER_L2) && !defined(HAS_CROWPANEL_35) +#if !defined(HAS_WIO_TRACKER_L2) && !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) s_kb_rot_left_btn = makeRotBtn(LV_SYMBOL_REFRESH, kbRotLeftCb); s_kb_rot_right_btn = makeRotBtn(LV_SYMBOL_REFRESH, kbRotRightCb); #endif @@ -69932,7 +69938,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no #else s_lock_on_screen_off = touchPrefsGetLockOnScreenOff(); #endif -#if !defined(HAS_CROWPANEL_35) // no unlock control there, so no lock to put a PIN on +#if !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) // no unlock control there, so no lock to put a PIN on s_pin_on = touchPrefsPinIsSet(); pinRestoreFails(); #endif @@ -70078,6 +70084,12 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no #endif g_draw_buf_px = 12800; band_psram = true; +#elif defined(HAS_CROWPANEL_50) + // 20 full rows of the 800-px panel in internal RAM, as Camillia-MT runs it: the + // RGB panel already scans its framebuffer out of PSRAM continuously, and a PSRAM + // band on top of that starves the LCD_CAM DMA and tears the picture. + const int draw_band_w = 800; + g_draw_buf_px = draw_band_w * 20; #elif defined(HELTEC_LORA_V4_R8) // The R8's async flush byte-swaps each band into a 6144-px internal DMA buffer // (LGFX_SWAP_BUF_PX) and the band has to fit it, so it stays 240x24; in PSRAM all the @@ -70306,6 +70318,9 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no #elif defined(HAS_CROWPANEL_35) g_lv.disp_drv.hor_res = s_remote_mode ? (s_remote_landscape ? 400 : 240) : (ui_landscape ? 480 : 320); g_lv.disp_drv.ver_res = s_remote_mode ? (s_remote_landscape ? 240 : 400) : (ui_landscape ? 320 : 480); +#elif defined(HAS_CROWPANEL_50) + g_lv.disp_drv.hor_res = s_remote_mode ? (s_remote_landscape ? 400 : 240) : (ui_landscape ? 800 : 480); + g_lv.disp_drv.ver_res = s_remote_mode ? (s_remote_landscape ? 240 : 400) : (ui_landscape ? 480 : 800); #else // Landscape rotates the panel in HARDWARE (smooth — no per-pixel software // rotation each flush), so tell LVGL the already-rotated resolution and let @@ -70378,7 +70393,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no // back to LVGL in the panel frame. heltecV4CapTouchSetPointRotation(s_ui_rotation); // LVGL won't, so driver does } -#if defined(HAS_CROWPANEL_35) || defined(ATTAKY_MESH_SERIES) +#if defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) || defined(ATTAKY_MESH_SERIES) else if (!s_remote_mode) { // A file-backed portrait preference may not have been loaded at the boot logo. // The logo reads the legacy NVS copy before main.cpp moves prefs to SPIFFS. On @@ -71706,8 +71721,8 @@ static inline void touchScreenBacklight(bool on) { ledcWrite(kM9BlPwmChannel, 255); // inverted: 255 = 0% conduction = off touchPanelSleep(true); } -#elif defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) - // The display owns its backlight (I2C on Wio, PWM on CrowPanel). +#elif defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) + // The display owns its backlight (I2C on Wio and the CrowPanel 5.0, PWM on the 3.5). if (on) applyBrightness(s_brightness_pct); else display.setBrightness(0); #elif defined(HAS_TDISPLAY_P4) @@ -72189,7 +72204,7 @@ void UITask::wakeScreen() { } void UITask::lockScreen() { -#if defined(HAS_CROWPANEL_35) +#if defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) // No physical unlock button: imported lock preferences must still allow touch wake. sleepScreen(); return; @@ -74405,7 +74420,7 @@ void UITask::loop() { static bool s_con_input_up = false; if (!s_con_input_up) { if (heltecV4CapTouchBegin()) { -#if !defined(HAS_CROWPANEL_35) +#if !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) heltecV4CapTouchStartBackgroundPoll(8); #endif s_con_input_up = true; @@ -75077,7 +75092,7 @@ void UITask::loop() { // thing with or without it. ssaverStart(now, false); _screen_off = true; -#if !defined(HAS_CROWPANEL_35) // no unlock control there; its lockScreen() only sleeps too +#if !defined(HAS_CROWPANEL_35) && !defined(HAS_CROWPANEL_50) // no unlock control there; its lockScreen() only sleeps too _manual_lock = s_lock_on_screen_off || s_pin_on; #endif } else @@ -75258,9 +75273,10 @@ void UITask::loop() { } if (g_lv.touch_inited) { pushDiagLine("touch init ok"); - #if defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) + #if defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_CROWPANEL_50) // GT911 (LovyanGFX) and the TCA9535/ADS1115 (Wire) share I2C0 on - // Wio's GPIO47/48; the CrowPanel's GT911 shares I2C0 on GPIO15/16. + // Wio's GPIO47/48; the CrowPanels' GT911 shares I2C0 on GPIO15/16 (with + // the 5.0's backlight controller). // Keep every transaction on loopTask so polling cannot interrupt Wire. pushDiagLine("touch inline (shared I2C)"); #elif defined(HAS_TDECK_PRO) diff --git a/src/ui-touch/device_caps.h b/src/ui-touch/device_caps.h index 06e3c61..a684b22 100644 --- a/src/ui-touch/device_caps.h +++ b/src/ui-touch/device_caps.h @@ -36,6 +36,19 @@ #define CAP_LOCK_SCREEN 0 // GPIO0 is LoRa NSS, no separate button to unlock #define CAP_BATTERY 0 // no battery voltage sensing +#elif defined(HAS_CROWPANEL_50) // ===== Elecrow CrowPanel Advance 5.0 (ESP32-S3) ===== + #define CAP_TOUCH 1 // GT911 capacitive touch + #define CAP_ROTATABLE 1 // ST7262 RGB panel, native 800x480 landscape = UI rotation 3 + #define CAP_LARGE_SCREEN 1 // native 800x480: large-panel layouts and the UI-size fonts + // The function DIP switch gives GPIO4/5/6 to the radio, so the card slot is dead + // while the radio works; File Transfer uses the internal tiles partition instead. + #define CAP_SD 0 + #define CAP_FILESYSTEM 0 + #define CAP_GPS 0 // no free GPIO pair for a UART (the RGB bus takes 17/18) + #define CAP_OTA 1 + #define CAP_LOCK_SCREEN 0 // no user button to unlock with + #define CAP_BATTERY 0 // no battery voltage sensing + #elif defined(HAS_WIO_TRACKER_L2) // ===== Seeed Wio Tracker L2 (ESP32-S3) ===== #define CAP_TOUCH 1 #define CAP_ROTATABLE 0 diff --git a/src/ui-touch/ui_font_18.c b/src/ui-touch/ui_font_18.c index 46e6c44..9aa61db 100644 --- a/src/ui-touch/ui_font_18.c +++ b/src/ui-touch/ui_font_18.c @@ -1,4 +1,4 @@ -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) /******************************************************************************* * Size: 18 px @@ -2683,4 +2683,4 @@ lv_font_t lv_font_montserrat_18 = { #endif /*#if WADA_UI_FONT_18_BODY*/ -#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ +#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ diff --git a/src/ui-touch/ui_font_20.c b/src/ui-touch/ui_font_20.c index c891bcb..9df0e6f 100644 --- a/src/ui-touch/ui_font_20.c +++ b/src/ui-touch/ui_font_20.c @@ -1,4 +1,4 @@ -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) /******************************************************************************* * Size: 20 px @@ -3013,4 +3013,4 @@ lv_font_t lv_font_montserrat_20 = { #endif /*#if WADA_UI_FONT_20_BODY*/ -#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ +#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ diff --git a/src/ui-touch/ui_font_24.c b/src/ui-touch/ui_font_24.c index 735433d..ccd418a 100644 --- a/src/ui-touch/ui_font_24.c +++ b/src/ui-touch/ui_font_24.c @@ -1,4 +1,4 @@ -#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) +#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) /******************************************************************************* * Size: 24 px @@ -3708,4 +3708,4 @@ lv_font_t lv_font_montserrat_24 = { #endif /*#if WADA_UI_FONT_24_BODY*/ -#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ +#endif /* defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HAS_CROWPANEL_50) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE) */ diff --git a/variants/crowpanel_50/CrowPanel50Board.h b/variants/crowpanel_50/CrowPanel50Board.h new file mode 100644 index 0000000..b902f1f --- /dev/null +++ b/variants/crowpanel_50/CrowPanel50Board.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include +#include + +// Elecrow CrowPanel Advance 5.0" (ESP32-S3-WROOM-1-N16R8). The expansion slot +// is routed to the SX1262 by the DIP switch on the back of the board, not by a +// GPIO, so there is nothing to set up here beyond the shared I2C bus that +// ESP32Board::begin() opens on GPIO15/16 (GT911 + backlight controller). +class CrowPanel50Board : public ESP32Board { +public: + void begin() { + ESP32Board::begin(); + } + + const char* getManufacturerName() const override { return "Elecrow CrowPanel Advance 5.0"; } +}; diff --git a/variants/crowpanel_50/CrowPanel50Display.cpp b/variants/crowpanel_50/CrowPanel50Display.cpp new file mode 100644 index 0000000..d4cf472 --- /dev/null +++ b/variants/crowpanel_50/CrowPanel50Display.cpp @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#include "CrowPanel50Display.h" + +#include +#include + +// Bus pins, timings and the board controller follow Elecrow's 5.0" factory code +// (LovyanGFX_Driver.h, lesson-05) and Meshtastic's LGFX_ELECROW70, as collected in +// Camillia-MT's hw_crowpanel_50.h / crowpanel_50_display.h. +namespace { +constexpr int kPanelW = 800, kPanelH = 480; +constexpr int kTouchSda = 15, kTouchScl = 16, kTouchInt = 1; + +// 14 MHz rather than Elecrow's 21: the panel scans out of the same PSRAM that +// LVGL, WiFi and the radio stacks use, and the lower clock leaves them the +// bandwidth that otherwise shows up as the picture drifting or tearing. +constexpr uint32_t kPclkHz = 14000000; + +// V1.1+ board MCU: single-byte commands at 0x30. 0 brightest .. 244 dimmest, +// 245 backlight off, 250 wakes the touch panel (V1.2/V1.3 values). +constexpr uint8_t kMcuAddr = 0x30; +constexpr uint8_t kMcuBlMax = 0, kMcuBlMin = 244, kMcuBlOff = 245, kMcuTouchOn = 250; + +// V1.0 PCA9557 (TCA9534 register layout). +constexpr uint8_t kIoexAddr = 0x18; +constexpr uint8_t kIoexBl = 1, kIoexTpRst = 2, kIoexAmpMute = 3, kIoexAmpShut = 4; +constexpr uint8_t kRegOutput = 0x01, kRegConfig = 0x03; +inline uint8_t ioBit(uint8_t pin) { return (uint8_t)(1u << pin); } +} // namespace + +// ---- Board controller ------------------------------------------------------- + +bool CrowPanel50BoardCtl::probe(uint8_t addr) { + Wire.beginTransmission(addr); + return Wire.endTransmission() == 0; +} + +CrowPanel50BoardCtl::Kind CrowPanel50BoardCtl::begin() { + if (probe(kIoexAddr)) { + _kind = PCA9557; + // Levels first, then direction, so no pin glitches on the way to output. + // Elecrow's start-up state: backlight on, touch out of reset, amplifier off. + _out = ioBit(kIoexBl) | ioBit(kIoexTpRst) | ioBit(kIoexAmpShut); + pcaWrite(kRegOutput, _out); + pcaWrite(kRegConfig, (uint8_t)~(ioBit(kIoexBl) | ioBit(kIoexTpRst) | ioBit(kIoexAmpMute) | ioBit(kIoexAmpShut))); + Serial.printf("[crowpanel-50] V1.0 board: PCA9557 at 0x%02X\n", kIoexAddr); + if (P_LORA_NSS != 0) + Serial.println("[crowpanel-50] V1.0 boards wire the radio's CS to GPIO0; this build uses " + "GPIO8 (V1.2+), so the radio will not answer"); + } else if (probe(kMcuAddr)) { + _kind = MCU; + Serial.printf("[crowpanel-50] V1.1+ board: MCU at 0x%02X\n", kMcuAddr); + } else { + _kind = NONE; + Serial.printf("[crowpanel-50] no backlight controller at 0x%02X or 0x%02X\n", kIoexAddr, kMcuAddr); + } + return _kind; +} + +// GT911 address strap: INT held low through the reset gives 0x5D. Elecrow's V1.2 +// code repeats the MCU's wake until the GT911 answers, so this does too, a few +// times. If the strap is missed it comes up at 0x14 instead; either is accepted. +uint8_t CrowPanel50BoardCtl::resetTouch() { + if (_kind == NONE) return probe(0x5D) ? 0x5D : (probe(0x14) ? 0x14 : 0); + for (int attempt = 1; attempt <= 5; ++attempt) { + pinMode(kTouchInt, OUTPUT); + digitalWrite(kTouchInt, LOW); + if (_kind == PCA9557) { + pcaSet(kIoexTpRst, false); + delay(20); + pcaSet(kIoexTpRst, true); + delay(100); + } else { + mcuSendAlways(kMcuTouchOn); + delay(120); + } + pinMode(kTouchInt, INPUT); + delay(100); + const bool at5d = probe(0x5D), at14 = probe(0x14); + if (at5d || at14) { + Serial.printf("[crowpanel-50] GT911 at 0x%02X after %d attempt%s\n", + at5d ? 0x5D : 0x14, attempt, attempt == 1 ? "" : "s"); + return at5d ? 0x5D : 0x14; + } + } + Serial.println("[crowpanel-50] GT911 did not answer at 0x5D or 0x14"); + return 0; +} + +// 0 is off. V1.0's backlight only switches; the MCU dims (255 brightest). Below +// full the MCU's PWM can make the backlight driver whine, so 100% is steady DC. +void CrowPanel50BoardCtl::setBacklight(uint8_t level) { + if (_kind == PCA9557) { + pcaSet(kIoexBl, level > 0); + } else if (_kind == MCU) { + if (level == 0) { mcuSend(kMcuBlOff); return; } + const uint8_t span = kMcuBlMin - kMcuBlMax; + mcuSend((uint8_t)(kMcuBlMin - (uint32_t)(level - 1) * span / 254)); + } +} + +void CrowPanel50BoardCtl::pcaSet(uint8_t pin, bool high) { + const uint8_t next = high ? (uint8_t)(_out | ioBit(pin)) : (uint8_t)(_out & ~ioBit(pin)); + if (next == _out) return; + _out = next; + pcaWrite(kRegOutput, _out); +} + +void CrowPanel50BoardCtl::pcaWrite(uint8_t reg, uint8_t value) { + Wire.beginTransmission(kIoexAddr); + Wire.write(reg); + Wire.write(value); + Wire.endTransmission(); +} + +void CrowPanel50BoardCtl::mcuSend(uint8_t cmd) { + if (cmd == _lastMcu) return; + if (mcuSendAlways(cmd)) _lastMcu = cmd; +} + +bool CrowPanel50BoardCtl::mcuSendAlways(uint8_t cmd) { + Wire.beginTransmission(kMcuAddr); + Wire.write(cmd); + return Wire.endTransmission() == 0; +} + +// ---- Display ------------------------------------------------------------------ + +CrowPanel50Display::CrowPanel50Display() : DisplayDriver(kPanelW, kPanelH) { + { + auto cfg = _panel.config(); + cfg.memory_width = kPanelW; + cfg.memory_height = kPanelH; + cfg.panel_width = kPanelW; + cfg.panel_height = kPanelH; + cfg.offset_x = 0; + cfg.offset_y = 0; + cfg.offset_rotation = 0; + cfg.readable = true; // the framebuffer is the only copy of the screen + _panel.config(cfg); + } + { + auto cfg = _panel.config_detail(); + cfg.use_psram = 1; // 800x480x2 = 750 KB + _panel.config_detail(cfg); + } + { + auto cfg = _bus.config(); + cfg.panel = &_panel; + cfg.pin_d0 = GPIO_NUM_21; // B0 + cfg.pin_d1 = GPIO_NUM_47; // B1 + cfg.pin_d2 = GPIO_NUM_48; // B2 + cfg.pin_d3 = GPIO_NUM_45; // B3 + cfg.pin_d4 = GPIO_NUM_38; // B4 + cfg.pin_d5 = GPIO_NUM_9; // G0 + cfg.pin_d6 = GPIO_NUM_10; // G1 + cfg.pin_d7 = GPIO_NUM_11; // G2 + cfg.pin_d8 = GPIO_NUM_12; // G3 + cfg.pin_d9 = GPIO_NUM_13; // G4 + cfg.pin_d10 = GPIO_NUM_14; // G5 + cfg.pin_d11 = GPIO_NUM_7; // R0 + cfg.pin_d12 = GPIO_NUM_17; // R1 + cfg.pin_d13 = GPIO_NUM_18; // R2 + cfg.pin_d14 = GPIO_NUM_3; // R3 + cfg.pin_d15 = GPIO_NUM_46; // R4 + cfg.pin_henable = GPIO_NUM_42; + cfg.pin_vsync = GPIO_NUM_41; + cfg.pin_hsync = GPIO_NUM_40; + cfg.pin_pclk = GPIO_NUM_39; + cfg.freq_write = kPclkHz; + cfg.hsync_polarity = 0; + cfg.hsync_front_porch = 8; + cfg.hsync_pulse_width = 4; + cfg.hsync_back_porch = 8; + cfg.vsync_polarity = 0; + cfg.vsync_front_porch = 8; + cfg.vsync_pulse_width = 4; + cfg.vsync_back_porch = 8; + cfg.pclk_idle_high = 1; + _bus.config(cfg); + } + _panel.setBus(&_bus); + + _light.setController(&_ctl); + _panel.setLight(&_light); + + { + auto cfg = _touch.config(); + cfg.x_min = 0; + cfg.x_max = kPanelW - 1; + cfg.y_min = 0; + cfg.y_max = kPanelH - 1; + cfg.pin_int = -1; // polled; INT is only the address strap + cfg.pin_rst = -1; // reset belongs to the board controller (resetTouch) + cfg.bus_shared = true; // the controller is on the same I2C pins + cfg.offset_rotation = 0; + cfg.i2c_port = 0; + cfg.i2c_addr = 0x5D; + cfg.pin_sda = kTouchSda; + cfg.pin_scl = kTouchScl; + cfg.freq = 400000; + _touch.config(cfg); + _panel.setTouch(&_touch); + } + _lcd.setPanel(&_panel); +} + +bool CrowPanel50Display::begin() { + if (_isOn) return true; + // board.begin() has opened Wire on GPIO15/16 already. + _ctl.begin(); + _touchAddr = _ctl.resetTouch(); + if (_touchAddr) { + auto cfg = _touch.config(); + cfg.i2c_addr = _touchAddr; + _touch.config(cfg); + } + if (!_lcd.init()) { + Serial.println("[crowpanel-50] RGB panel init failed"); + return false; + } + _lcd.setSwapBytes(true); + setDisplayRotation(3); + _lcd.fillScreen(0); + // LovyanGFX's touch init reprograms I2C0 under Wire; take the bus back for the + // backlight controller and the RTC probe, as the CrowPanel 3.5 does. + Wire.end(); + if (!Wire.begin(kTouchSda, kTouchScl, 400000)) { + Serial.println("[crowpanel-50] I2C bus init failed"); + return false; + } + _ctl.setBacklight(_brightness); + _isOn = true; + Serial.printf("[crowpanel-50] display %dx%d, touch %s\n", width(), height(), + _lcd.touch() ? "up" : "not found"); + return true; +} + +void CrowPanel50Display::turnOn() { + if (!_isOn) { _ctl.setBacklight(_brightness); _isOn = true; } +} +void CrowPanel50Display::turnOff() { + if (_isOn) { _ctl.setBacklight(0); _isOn = false; } +} +void CrowPanel50Display::clear() { _lcd.fillScreen(0); } + +// The UI's portrait is 0 and its upright landscape 3 (ROT_270), as on the 3.5. +// The panel itself is native 800x480 landscape at LovyanGFX rotation 0, so map +// 3 to that, 1 to landscape turned 180 degrees and 0 to portrait. LovyanGFX +// rotates the framebuffer writes and the GT911 coordinates together. +void CrowPanel50Display::setDisplayRotation(uint8_t rotation) { + uint8_t r; + switch (rotation) { + case 3: r = 0; break; + case 1: r = 2; break; + default: r = 1; break; + } + _lcd.setRotation(r); + setLogicalSize(_lcd.width(), _lcd.height()); +} + +void CrowPanel50Display::startFrame(ColorVal bkg) { _lcd.fillScreen(bkg); } +void CrowPanel50Display::setTextSize(int sz) { _lcd.setTextSize(sz); } +void CrowPanel50Display::setColor(ColorVal c) { _color = c; _lcd.setTextColor(c); } +void CrowPanel50Display::setCursor(int x, int y) { _lcd.setCursor(x, y); } +void CrowPanel50Display::print(const char* str) { _lcd.print(str); } +void CrowPanel50Display::fillRect(int x, int y, int w, int h) { _lcd.fillRect(x, y, w, h, _color); } +void CrowPanel50Display::drawRect(int x, int y, int w, int h) { _lcd.drawRect(x, y, w, h, _color); } +void CrowPanel50Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { + _lcd.drawXBitmap(x, y, bits, w, h, _color); +} +uint16_t CrowPanel50Display::getTextWidth(const char* str) { return _lcd.textWidth(str); } +void CrowPanel50Display::endFrame() {} + +void CrowPanel50Display::writePixelsRGB565(int x, int y, int w, int h, const uint16_t* pixels) { + if (!_isOn || !pixels || w <= 0 || h <= 0) return; + _lcd.startWrite(); + _lcd.setAddrWindow(x, y, w, h); + _lcd.writePixels(const_cast(pixels), (uint32_t)w * h); + _lcd.endWrite(); +} + +void CrowPanel50Display::setBrightness(uint8_t value) { + if (value) _brightness = value; + _ctl.setBacklight(value); +} + +bool CrowPanel50Display::getTouchPoint(uint16_t& x, uint16_t& y) { + int32_t tx = 0, ty = 0; + if (!_lcd.getTouch(&tx, &ty)) return false; + x = (uint16_t)tx; + y = (uint16_t)ty; + return true; +} diff --git a/variants/crowpanel_50/CrowPanel50Display.h b/variants/crowpanel_50/CrowPanel50Display.h new file mode 100644 index 0000000..def4818 --- /dev/null +++ b/variants/crowpanel_50/CrowPanel50Display.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include + +#define LGFX_USE_V1 +#include +#include +#include + +// Whatever owns the backlight and the GT911 reset on this board. V1.0 has a +// PCA9557 expander at 0x18; from V1.1 a small board MCU at 0x30 takes both over +// (and the buzzer/speaker). begin() tells them apart by which address answers, +// so one build drives either. Ported from Camillia-MT's crowpanel_50_display.h. +class CrowPanel50BoardCtl { +public: + enum Kind : uint8_t { NONE, PCA9557, MCU }; + + Kind begin(); + uint8_t resetTouch(); // 0x5D / 0x14 the GT911 answered at, 0 if neither + void setBacklight(uint8_t level); + Kind kind() const { return _kind; } + +private: + static bool probe(uint8_t addr); + void pcaSet(uint8_t pin, bool high); + void pcaWrite(uint8_t reg, uint8_t value); + void mcuSend(uint8_t cmd); + bool mcuSendAlways(uint8_t cmd); + + Kind _kind = NONE; + uint8_t _out = 0; + int16_t _lastMcu = -1; +}; + +class CrowPanel50Light : public lgfx::v1::ILight { +public: + void setController(CrowPanel50BoardCtl* ctl) { _ctl = ctl; } + bool init(uint8_t brightness) override { setBrightness(brightness); return true; } + void setBrightness(uint8_t brightness) override { if (_ctl) _ctl->setBacklight(brightness); } + +private: + CrowPanel50BoardCtl* _ctl = nullptr; +}; + +class CrowPanel50Display : public DisplayDriver { +public: + CrowPanel50Display(); + bool begin(); + bool isOn() override { return _isOn; } + void turnOn() override; + void turnOff() override; + void clear() override; + void startFrame(ColorVal bkg = UIColor::window_bkg) override; + void setTextSize(int sz) override; + void setColor(ColorVal c) override; + void setCursor(int x, int y) override; + void print(const char* str) override; + void fillRect(int x, int y, int w, int h) override; + void drawRect(int x, int y, int w, int h) override; + void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; + uint16_t getTextWidth(const char* str) override; + void endFrame() override; + + void setDisplayRotation(uint8_t rotation); + void setBrightness(uint8_t value); + bool getTouchPoint(uint16_t& x, uint16_t& y); + void writePixelsRGB565(int x, int y, int w, int h, const uint16_t* pixels); + uint8_t touchAddr() const { return _touchAddr; } + +private: + lgfx::Bus_RGB _bus; + lgfx::Panel_RGB _panel; + CrowPanel50Light _light; + lgfx::Touch_GT911 _touch; + lgfx::LGFX_Device _lcd; + CrowPanel50BoardCtl _ctl; + uint8_t _touchAddr = 0; + uint8_t _brightness = 255; + bool _isOn = false; + uint16_t _color = 0xffff; +}; diff --git a/variants/crowpanel_50/target.cpp b/variants/crowpanel_50/target.cpp new file mode 100644 index 0000000..0921c9a --- /dev/null +++ b/variants/crowpanel_50/target.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#include +#include "target.h" + +CrowPanel50Board board; +static SPIClass radioSpi(FSPI); +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, radioSpi); +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +ClockFloorRTC rtc_clock(fallback_clock); + +// No GPS: every GPIO a UART could use is taken by the RGB panel, the radio or +// the touch bus (an unpinned Serial1 would fall back to GPIO15/16, the GT911). +EnvironmentSensorManager sensors; + +CrowPanel50Display display; + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + if (!radio.std_init(&radioSpi)) { + // The DIP switch on the back shares the slot's pins with the SD card and the + // speaker/microphone; only its wireless-module position connects the radio. + Serial.printf("[crowpanel-50] SX1262 not found (CS%d). Set the function DIP switch to the " + "wireless module and seat the module in J8/J9.\n", P_LORA_NSS); + return false; + } + return true; +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/crowpanel_50/target.h b/variants/crowpanel_50/target.h new file mode 100644 index 0000000..1d28d2f --- /dev/null +++ b/variants/crowpanel_50/target.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include "../../src/helpers/ClockFloorRTC.h" +#include "helpers/sensors/EnvironmentSensorManager.h" +#include "CrowPanel50Board.h" +#include "CrowPanel50Display.h" + +extern CrowPanel50Board board; +extern RADIO_CLASS radio; +extern WRAPPER_CLASS radio_driver; +extern ClockFloorRTC rtc_clock; +extern EnvironmentSensorManager sensors; +extern CrowPanel50Display display; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity();