diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..72d95970a --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# Absolute path to the local meshtastic config.yaml file +CONFIG_PATH=/path/to/meshtastic/config.yaml +# USB device to passthrough (`lsusb -t`: look for `ch341`) +USB_DEVICE=/dev/bus/usb/001/037 diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index bb5a394fd..13817a8cf 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -10,12 +10,6 @@ jobs: build-native: runs-on: ubuntu-latest steps: - - name: Install libs needed for native build - shell: bash - run: | - sudo apt-get update --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev - - name: Checkout code uses: actions/checkout@v4 with: @@ -23,21 +17,6 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Native - run: bin/build-native.sh - - name: Get release version string run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version diff --git a/Dockerfile b/Dockerfile index ca216e04b..f3b294a5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,29 @@ -FROM debian:bookworm-slim AS builder +# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue +# trunk-ignore-all(hadolint/DL3008): Use latest version of apt packages for buildchain +# trunk-ignore-all(trivy/DS002): We must run as root for this container +# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container +# trunk-ignore-all(hadolint/DL3002): We must run as root for this container +FROM python:3.12-bookworm AS builder ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC -# http://bugs.python.org/issue19846 -# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. -ENV LANG C.UTF-8 - -# Install build deps -USER root - -# trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue -# trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain -RUN apt-get update && apt-get install --no-install-recommends -y wget python3 python3-pip python3-wheel python3-venv g++ zip git \ - ca-certificates libgpiod-dev libyaml-cpp-dev libbluetooth-dev \ - libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \ - apt-get clean && rm -rf /var/lib/apt/lists/* && mkdir /tmp/firmware - -RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh && chown mesh:mesh /tmp/firmware -USER mesh +# Install Dependencies +ENV PIP_ROOT_USER_ACTION=ignore +RUN apt-get update && apt-get install --no-install-recommends -y wget g++ zip git ca-certificates \ + libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev \ + libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir -U platformio==6.1.16 && \ + mkdir /tmp/firmware +# Copy source code WORKDIR /tmp/firmware -RUN python3 -m venv /tmp/firmware -RUN bash -o pipefail -c "source bin/activate; pip3 install --no-cache-dir -U platformio==6.1.15" -# trunk-ignore(terrascan/AC_DOCKER_00024): We would actually like these files to be owned by mesh tyvm -COPY --chown=mesh:mesh . /tmp/firmware -RUN bash -o pipefail -c "source ./bin/activate && bash ./bin/build-native.sh" -RUN cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" +COPY . /tmp/firmware + +# Build +RUN bash ./bin/build-native.sh && \ + cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" ##### PRODUCTION BUILD ############# @@ -35,20 +32,25 @@ FROM debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC -# trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue -# trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain -RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ - apt-get clean && rm -rf /var/lib/apt/lists/* +# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root +USER root -RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh -USER mesh +RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ + apt-get clean && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /var/lib/meshtasticd \ + && mkdir -p /etc/meshtasticd/config.d -WORKDIR /home/mesh -COPY --from=builder /tmp/firmware/release/meshtasticd /home/mesh/ +# Fetch compiled binary from the builder +COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ +# Copy config templates +COPY ./bin/config.d /etc/meshtasticd/available.d -RUN mkdir data -VOLUME /home/mesh/data +WORKDIR /var/lib/meshtasticd +VOLUME /var/lib/meshtasticd -CMD [ "sh", "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ] +# Expose Meshtastic TCP API port from the host +EXPOSE 4403 + +CMD [ "sh", "-cx", "meshtasticd -d /var/lib/meshtasticd" ] HEALTHCHECK NONE \ No newline at end of file diff --git a/alpine.Dockerfile b/alpine.Dockerfile new file mode 100644 index 000000000..115602b3b --- /dev/null +++ b/alpine.Dockerfile @@ -0,0 +1,42 @@ +# trunk-ignore-all(trivy/DS002): We must run as root for this container +# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container +# trunk-ignore-all(hadolint/DL3002): We must run as root for this container + +FROM python:3.12-alpine3.21 AS builder + +ENV PIP_ROOT_USER_ACTION=ignore +RUN apk add bash g++ libstdc++-dev linux-headers zip git ca-certificates libgpiod-dev yaml-cpp-dev bluez-dev \ + libusb-dev i2c-tools-dev openssl-dev pkgconf argp-standalone && \ + pip install --no-cache-dir -U platformio==6.1.16 && \ + mkdir /tmp/firmware + +WORKDIR /tmp/firmware +COPY . /tmp/firmware + +# Create small package (no debugging symbols) +# Add `argp` for musl +ENV PLATFORMIO_BUILD_FLAGS="-Os -ffunction-sections -fdata-sections -Wl,--gc-sections -largp" + +RUN bash ./bin/build-native.sh && \ + cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd" + +# ##### PRODUCTION BUILD ############# + +FROM alpine:3.21 + +# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root +USER root + +RUN apk add libstdc++ libgpiod yaml-cpp libusb i2c-tools \ + && mkdir -p /var/lib/meshtasticd \ + && mkdir -p /etc/meshtasticd/config.d +COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ + +WORKDIR /var/lib/meshtasticd +VOLUME /var/lib/meshtasticd + +EXPOSE 4403 + +CMD [ "sh", "-cx", "meshtasticd --fsdir=/var/lib/meshtasticd" ] + +HEALTHCHECK NONE \ No newline at end of file diff --git a/boards/mesh-tab.json b/boards/mesh-tab.json new file mode 100644 index 000000000..52c65bf77 --- /dev/null +++ b/boards/mesh-tab.json @@ -0,0 +1,42 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_16MB.csv", + "memory_type": "qio_qspi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x80D6"]], + "mcu": "esp32s3", + "variant": "mesh-tab" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "ESP32-S3 WROOM-1 N16R2 (16 MB FLASH, 2 MB PSRAM)", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://github.com/valzzu/Mesh-Tab", + "vendor": "Espressif" +} diff --git a/boards/seeed-sensecap-indicator.json b/boards/seeed-sensecap-indicator.json index 3fc57126f..0a02fc882 100644 --- a/boards/seeed-sensecap-indicator.json +++ b/boards/seeed-sensecap-indicator.json @@ -15,10 +15,12 @@ ], "f_cpu": "240000000L", "f_flash": "80000000L", + "f_boot": "120000000L", + "boot": "qio", "flash_mode": "qio", "hwids": [["0x1A86", "0x7523"]], "mcu": "esp32s3", - "variant": "esp32s3r8" + "variant": "esp32s3" }, "connectivity": ["wifi", "bluetooth", "lora"], "debug": { @@ -32,9 +34,9 @@ "flash_size": "8MB", "maximum_ram_size": 327680, "maximum_size": 8388608, - "require_upload_port": true, + "require_upload_port": false, "use_1200bps_touch": true, - "wait_for_upload_port": true, + "wait_for_upload_port": false, "speed": 921600 }, "url": "https://www.seeedstudio.com/Indicator-for-Meshtastic.html", diff --git a/boards/t-deck.json b/boards/t-deck.json index d62ec48e6..b112921b9 100644 --- a/boards/t-deck.json +++ b/boards/t-deck.json @@ -10,7 +10,7 @@ "-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_MODE=0", "-DARDUINO_RUNNING_CORE=1", - "-DARDUINO_EVENT_RUNNING_CORE=0" + "-DARDUINO_EVENT_RUNNING_CORE=1" ], "f_cpu": "240000000L", "f_flash": "80000000L", diff --git a/docker-compose.yml b/docker-compose.yml index 82f2647e8..4aac318c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,26 @@ -version: "3.7" +# USB-Based Meshtastic container-node! + +# Copy .env.example to .env and set the USB_DEVICE and CONFIG_PATH variables services: meshtastic-node: build: . - deploy: - mode: replicated - replicas: 4 - networks: - - mesh + container_name: meshtasticd -networks: - mesh: + # Pass USB device through to the container + devices: + - "${USB_DEVICE}" + + # Mount local config file and named volume for data persistence + volumes: + - "${CONFIG_PATH}:/etc/meshtasticd/config.yaml:ro" + - meshtastic_data:/var/lib/meshtasticd + + # Forward the container’s port 4403 to the host + ports: + - 4403:4403 + + restart: unless-stopped + +volumes: + meshtastic_data: diff --git a/platformio.ini b/platformio.ini index 41f1ca764..bf50b7646 100644 --- a/platformio.ini +++ b/platformio.ini @@ -81,7 +81,8 @@ build_flags = -Wno-missing-field-initializers -DRADIOLIB_EXCLUDE_LORAWAN=1 -DMESHTASTIC_EXCLUDE_DROPZONE=1 -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 - -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware + -DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1 + -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware #-DBUILD_EPOCH=$UNIX_TIME ;-D OLED_PL @@ -153,7 +154,6 @@ lib_deps = sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@1.2.13 ClosedCube OPT3001@1.1.2 emotibit/EmotiBit MLX90632@1.0.8 - sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2 adafruit/Adafruit MLX90614 Library@2.1.5 https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502 boschsensortec/BME68x Sensor Library@1.1.40407 @@ -161,4 +161,7 @@ lib_deps = mprograms/QMC5883LCompass@1.2.3 dfrobot/DFRobot_RTU@1.0.3 https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d - robtillaart/INA226@0.6.0 + robtillaart/INA226@0.6.0 + + ; Health Sensor Libraries + sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2 \ No newline at end of file diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 6cd17dac8..df46c1941 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -55,6 +55,15 @@ extern "C" void lfs_assert(const char *reason) { LOG_ERROR("LFS assert: %s", reason); lfs_assert_failed = true; + +#ifndef ARCH_PORTDUINO +#ifdef FSCom + // CORRUPTED FILESYSTEM. This causes bootloop so + // might as well try formatting now. + LOG_ERROR("Trying FSCom.format()"); + FSCom.format(); +#endif +#endif } /** diff --git a/src/configuration.h b/src/configuration.h index b5727508d..994f1e72e 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -250,6 +250,9 @@ along with this program. If not, see . #ifndef HAS_SCREEN #define HAS_SCREEN 0 #endif +#ifndef HAS_TFT +#define HAS_TFT 0 +#endif #ifndef HAS_WIRE #define HAS_WIRE 0 #endif @@ -312,6 +315,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_AUDIO 1 #define MESHTASTIC_EXCLUDE_DETECTIONSENSOR 1 #define MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR 1 +#define MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY 1 #define MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION 1 #define MESHTASTIC_EXCLUDE_PAXCOUNTER 1 #define MESHTASTIC_EXCLUDE_POWER_TELEMETRY 1 @@ -361,4 +365,4 @@ along with this program. If not, see . #endif #include "DebugConfiguration.h" -#include "RF95Configuration.h" \ No newline at end of file +#include "RF95Configuration.h" diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 6e695c22f..a786f874d 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -458,11 +458,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) i2cBus->endTransmission(); len = i2cBus->readBytes(info, 5); if (len == 5 && memcmp(expectedInfo, info, len) == 0) { - LOG_INFO("NXP SE050 crypto chip found\n"); + LOG_INFO("NXP SE050 crypto chip found"); type = NXP_SE050; } else { - LOG_INFO("FT6336U touchscreen found\n"); + LOG_INFO("FT6336U touchscreen found"); type = FT6336U; } break; @@ -510,4 +510,4 @@ void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address) { LOG_INFO("%s found at address 0x%x", device, address); } -#endif \ No newline at end of file +#endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 27ea6f414..4cfb8701e 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1718,7 +1718,7 @@ void Screen::setup() #endif serialSinceMsec = millis(); -#if ARCH_PORTDUINO +#if ARCH_PORTDUINO && !HAS_TFT if (settingsMap[touchscreenModule]) { touchScreenImpl1 = new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch); diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 87c3f7de9..4f2af670b 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -347,7 +347,7 @@ static LGFX *tft = nullptr; #include // Graphics and font library for ILI9342 driver chip static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h -#elif ARCH_PORTDUINO && HAS_SCREEN != 0 +#elif ARCH_PORTDUINO && HAS_SCREEN != 0 && !HAS_TFT #include // Graphics and font library for ST7735 driver chip class LGFX : public lgfx::LGFX_Device diff --git a/src/input/MPR121Keyboard.cpp b/src/input/MPR121Keyboard.cpp index 078d80272..9bca6801d 100644 --- a/src/input/MPR121Keyboard.cpp +++ b/src/input/MPR121Keyboard.cpp @@ -29,6 +29,8 @@ #define _MPR121_REG_CONFIG1 0x5C #define _MPR121_REG_CONFIG2 0x5D #define _MPR121_REG_ELECTRODE_CONFIG 0x5E +#define _MPR121_REG_AUTOCONF_CTRL0 0x7B +#define _MPR121_REG_AUTOCONF_CTRL1 0x7C #define _MPR121_REG_SOFT_RESET 0x80 #define _KEY_MASK 0x0FFF // Key mask for the first 12 bits @@ -87,7 +89,7 @@ uint8_t MPR121_KeyMap[12] = {2, 5, 8, 11, 1, 4, 7, 10, 0, 3, 6, 9}; MPR121Keyboard::MPR121Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr) { - // LOG_DEBUG("MPR121 @ %02x\n", m_addr); + // LOG_DEBUG("MPR121 @ %02x", m_addr); state = Init; last_key = -1; last_tap = 0L; @@ -132,18 +134,18 @@ void MPR121Keyboard::reset() writeRegister(_MPR121_REG_ELECTRODE_CONFIG, 0x00); delay(100); - LOG_DEBUG("MPR121 Configure"); + LOG_DEBUG("MPR121 Configuring"); // Set touch release thresholds for (uint8_t i = 0; i < 12; i++) { // Set touch threshold - writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 15); + writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 10); delay(20); // Set release threshold - writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 7); + writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 5); delay(20); } // Configure filtering and baseline registers - writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x01); + writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x05); delay(20); writeRegister(_MPR121_REG_MAX_HALF_DELTA_FALLING, 0x01); delay(20); @@ -153,7 +155,7 @@ void MPR121Keyboard::reset() delay(20); writeRegister(_MPR121_REG_NOISE_HALF_DELTA_TOUCHED, 0x00); delay(20); - writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x0e); + writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x05); delay(20); writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_FALLING, 0x01); delay(20); @@ -165,18 +167,19 @@ void MPR121Keyboard::reset() delay(20); writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_TOUCHED, 0x00); delay(20); - // Set Debounce to 0x02 - writeRegister(_MPR121_REG_DEBOUNCE, 0x00); + writeRegister(_MPR121_REG_AUTOCONF_CTRL0, 0x04); // Auto-config enable delay(20); - // Set Filter1 itterations and discharge current 6x and 16uA respectively (0x10) - writeRegister(_MPR121_REG_CONFIG1, 0x10); + writeRegister(_MPR121_REG_AUTOCONF_CTRL1, 0x00); // Ensure no auto-config interrupt delay(20); - // Set CDT to 0.5us, Filter2 itterations to 4x, and Sample interval = 0 (0x20) - writeRegister(_MPR121_REG_CONFIG2, 0x20); + writeRegister(_MPR121_REG_DEBOUNCE, 0x02); + delay(20); + writeRegister(_MPR121_REG_CONFIG1, 0x20); + delay(20); + writeRegister(_MPR121_REG_CONFIG2, 0x21); delay(20); // Enter run mode by Seting partial filter calibration tracking, disable proximity detection, enable 12 channels writeRegister(_MPR121_REG_ELECTRODE_CONFIG, - ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH); + ECR_CALIBRATION_TRACK_FROM_FULL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH); delay(100); LOG_DEBUG("MPR121 Run"); state = Idle; @@ -427,4 +430,4 @@ void MPR121Keyboard::writeRegister(uint8_t reg, uint8_t value) if (writeCallback) { writeCallback(m_addr, data[0], &(data[1]), 1); } -} +} \ No newline at end of file diff --git a/src/input/TouchScreenBase.cpp b/src/input/TouchScreenBase.cpp index a63203362..d2f7b54f8 100644 --- a/src/input/TouchScreenBase.cpp +++ b/src/input/TouchScreenBase.cpp @@ -113,13 +113,13 @@ int32_t TouchScreenBase::runOnce() if (_tapped) { _tapped = false; e.touchEvent = static_cast(TOUCH_ACTION_TAP); - LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + LOG_DEBUG("action TAP(%d/%d)", _last_x, _last_y); } } else { if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) { _tapped = false; e.touchEvent = static_cast(TOUCH_ACTION_TAP); - LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + LOG_DEBUG("action TAP(%d/%d)", _last_x, _last_y); } } #else @@ -156,4 +156,4 @@ void TouchScreenBase::hapticFeedback() drv.setWaveform(1, 0); // end waveform drv.go(); #endif -} \ No newline at end of file +} diff --git a/src/main.cpp b/src/main.cpp index f4bb11535..5982e709d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,6 +40,7 @@ #include #ifdef ARCH_ESP32 +#include "freertosinc.h" #if !MESHTASTIC_EXCLUDE_WEBSERVER #include "mesh/http/WebServer.h" #endif @@ -173,6 +174,8 @@ std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySenso Router *router = NULL; // Users of router don't care what sort of subclass implements that API +const char *firmware_version = optstr(APP_VERSION_SHORT); + const char *getDeviceName() { uint8_t dmac[6]; @@ -1275,4 +1278,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index e29c596df..f94540905 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -24,11 +24,15 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) printPacket("Ignore dupe incoming msg", p); rxDupe++; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER && - config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { + config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && + config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) { // cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater! if (Router::cancelSending(p->from, p->id)) txRelayCanceled++; } + if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) { + iface->clampToLateRebroadcastWindow(getFrom(p), p->id); + } /* If the original transmitter is doing retransmissions (hopStart equals hopLimit) for a reliable transmission, e.g., when the ACK got lost, we will handle the packet again to make sure it gets an ACK to its packet. */ diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 99ef41c1e..d7ee65800 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -16,6 +16,12 @@ inline uint32_t getPriority(const meshtastic_MeshPacket *p) bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_MeshPacket *p2) { assert(p1 && p2); + + // If one packet is in the late transmit window, prefer the other one + if ((bool)p1->tx_after != (bool)p2->tx_after) { + return !p1->tx_after; + } + auto p1p = getPriority(p1), p2p = getPriority(p2); // If priorities differ, use that // for equal priorities, prefer packets already on mesh. @@ -94,11 +100,11 @@ meshtastic_MeshPacket *MeshPacketQueue::getFront() } /** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */ -meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id) +meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id, bool tx_normal, bool tx_late) { for (auto it = queue.begin(); it != queue.end(); it++) { auto p = (*it); - if (getFrom(p) == from && p->id == id) { + if (getFrom(p) == from && p->id == id && ((tx_normal && !p->tx_after) || (tx_late && p->tx_after))) { queue.erase(it); return p; } @@ -114,9 +120,10 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) if (queue.empty()) { return false; // No packets to replace } + // Check if the packet at the back has a lower priority than the new packet auto &backPacket = queue.back(); - if (backPacket->priority < p->priority) { + if (!backPacket->tx_after && backPacket->priority < p->priority) { // Remove the back packet packetPool.release(backPacket); queue.pop_back(); @@ -125,6 +132,19 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) return true; } + if (backPacket->tx_after) { + // Check if there's a non-late packet with lower priority + auto it = queue.end(); + auto refPacket = *--it; + for (; refPacket->tx_after && it != queue.begin(); refPacket = *--it) + ; + if (!refPacket->tx_after && refPacket->priority < p->priority) { + packetPool.release(refPacket); + enqueue(refPacket); + return true; + } + } + // If the back packet's priority is not lower, no replacement occurs return false; } \ No newline at end of file diff --git a/src/mesh/MeshPacketQueue.h b/src/mesh/MeshPacketQueue.h index 3c28fc5ce..b41a214b9 100644 --- a/src/mesh/MeshPacketQueue.h +++ b/src/mesh/MeshPacketQueue.h @@ -36,5 +36,5 @@ class MeshPacketQueue meshtastic_MeshPacket *getFront(); /** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */ - meshtastic_MeshPacket *remove(NodeNum from, PacketId id); -}; + meshtastic_MeshPacket *remove(NodeNum from, PacketId id, bool tx_normal = true, bool tx_late = true); +}; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2af85e4f5..9dbe92b7c 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -57,6 +57,7 @@ NodeDB *nodeDB = nullptr; EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate; meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node; meshtastic_LocalConfig config; +meshtastic_DeviceUIConfig uiconfig{.screen_brightness = 153, .screen_timeout = 30}; meshtastic_LocalModuleConfig moduleConfig; meshtastic_ChannelFile channelFile; @@ -895,6 +896,7 @@ void NodeDB::pickNewNodeNum() static const char *prefFileName = "/prefs/db.proto"; static const char *configFileName = "/prefs/config.proto"; +static const char *uiconfigFileName = "/prefs/uiconfig.proto"; static const char *moduleConfigFileName = "/prefs/module.proto"; static const char *channelFileName = "/prefs/channels.proto"; @@ -1054,6 +1056,12 @@ void NodeDB::loadFromDisk() } } + state = loadProto(uiconfigFileName, meshtastic_DeviceUIConfig_size, sizeof(meshtastic_DeviceUIConfig), + &meshtastic_DeviceUIConfig_msg, &uiconfig); + if (state == LoadFileResult::LOAD_SUCCESS) { + LOG_INFO("Loaded UIConfig"); + } + // 2.4.X - configuration migration to update new default intervals if (moduleConfig.version < 23) { LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals", moduleConfig.version); diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 7e51a1240..c8c0d3170 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -29,6 +29,7 @@ extern meshtastic_DeviceState devicestate; extern meshtastic_ChannelFile channelFile; extern meshtastic_MyNodeInfo &myNodeInfo; extern meshtastic_LocalConfig config; +extern meshtastic_DeviceUIConfig uiconfig; extern meshtastic_LocalModuleConfig moduleConfig; extern meshtastic_User &owner; extern meshtastic_Position localPosition; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index f49718c5e..8c1ba74c7 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -164,6 +164,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) * * Our sending states progress in the following sequence (the client apps ASSUME THIS SEQUENCE, DO NOT CHANGE IT): STATE_SEND_MY_INFO, // send our my info record + STATE_SEND_UIDATA, STATE_SEND_OWN_NODEINFO, STATE_SEND_METADATA, STATE_SEND_CHANNELS @@ -188,7 +189,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_NOTHING: LOG_DEBUG("FromRadio=STATE_SEND_NOTHING"); break; - case STATE_SEND_MY_INFO: LOG_DEBUG("FromRadio=STATE_SEND_MY_INFO"); // If the user has specified they don't want our node to share its location, make sure to tell the phone @@ -196,11 +196,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag; strncpy(myNodeInfo.pio_env, optstr(APP_ENV), sizeof(myNodeInfo.pio_env)); fromRadioScratch.my_info = myNodeInfo; - state = STATE_SEND_OWN_NODEINFO; + state = STATE_SEND_UIDATA; service->refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon. break; + case STATE_SEND_UIDATA: + LOG_INFO("getFromRadio=STATE_SEND_UIDATA"); + fromRadioScratch.which_payload_variant = meshtastic_FromRadio_deviceuiConfig_tag; + fromRadioScratch.deviceuiConfig = uiconfig; + state = STATE_SEND_OWN_NODEINFO; + break; + case STATE_SEND_OWN_NODEINFO: { LOG_DEBUG("Send My NodeInfo"); auto us = nodeDB->readNextMeshNode(readIndex); @@ -284,6 +291,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) LOG_DEBUG("Send config: sessionkey"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_sessionkey_tag; break; + case meshtastic_Config_device_ui_tag: // NOOP! + fromRadioScratch.config.which_payload_variant = meshtastic_Config_device_ui_tag; + break; default: LOG_ERROR("Unknown config type %d", config_state); } @@ -518,6 +528,7 @@ bool PhoneAPI::available() case STATE_SEND_NOTHING: return false; case STATE_SEND_MY_INFO: + case STATE_SEND_UIDATA: case STATE_SEND_CHANNELS: case STATE_SEND_CONFIG: case STATE_SEND_MODULECONFIG: @@ -657,4 +668,4 @@ int PhoneAPI::onNotify(uint32_t newValue) } return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one -} \ No newline at end of file +} diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 3247fee5c..681b244c8 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -34,6 +34,7 @@ class PhoneAPI { enum State { STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config + STATE_SEND_UIDATA, // send stored data for device-ui STATE_SEND_MY_INFO, // send our my info record STATE_SEND_OWN_NODEINFO, STATE_SEND_METADATA, @@ -148,6 +149,9 @@ class PhoneAPI */ virtual void onNowHasData(uint32_t fromRadioNum) {} + /// begin a new connection + void handleStartConfig(); + private: void releasePhonePacket(); @@ -157,9 +161,6 @@ class PhoneAPI void releaseClientNotification(); - /// begin a new connection - void handleStartConfig(); - bool wasSeenRecently(uint32_t packetId); /** @@ -170,4 +171,4 @@ class PhoneAPI /// If the mesh service tells us fromNum has changed, tell the phone virtual int onNotify(uint32_t newValue) override; -}; \ No newline at end of file +}; diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 5161ac41f..b1403f3b6 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -254,8 +254,8 @@ uint32_t RadioInterface::getTxDelayMsec() return random(0, pow(2, CWsize)) * slotTimeMsec; } -/** The delay to use when we want to flood a message */ -uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) +/** The CW size to use when calculating SNR_based delays */ +uint8_t RadioInterface::getCWsize(float snr) { // The minimum value for a LoRa SNR const uint32_t SNR_MIN = -20; @@ -263,10 +263,24 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) // The maximum value for a LoRa SNR const uint32_t SNR_MAX = 15; + return map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax); +} + +/** The worst-case SNR_based packet delay */ +uint32_t RadioInterface::getTxDelayMsecWeightedWorst(float snr) +{ + uint8_t CWsize = getCWsize(snr); + // offset the maximum delay for routers: (2 * CWmax * slotTimeMsec) + return (2 * CWmax * slotTimeMsec) + pow(2, CWsize) * slotTimeMsec; +} + +/** The delay to use when we want to flood a message */ +uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) +{ // high SNR = large CW size (Long Delay) // low SNR = small CW size (Short Delay) uint32_t delay = 0; - uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax); + uint8_t CWsize = getCWsize(snr); // LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize); if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { @@ -313,6 +327,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) out += DEBUG_PORT.mt_sprintf(" failId=%08x", s.ackVariant.fail_id); */ } else { out += " encrypted"; + out += DEBUG_PORT.mt_sprintf(" len=%d", p->encrypted.size + sizeof(PacketHeader)); } if (p->rx_time != 0) @@ -622,4 +637,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} \ No newline at end of file +} diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 89a4c7087..652b2269c 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -173,9 +173,18 @@ class RadioInterface /** The delay to use when we want to send something */ uint32_t getTxDelayMsec(); + /** The CW to use when calculating SNR_based delays */ + uint8_t getCWsize(float snr); + + /** The worst-case SNR_based packet delay */ + uint32_t getTxDelayMsecWeightedWorst(float snr); + /** The delay to use when we want to flood a message. Use a weighted scale based on SNR */ uint32_t getTxDelayMsecWeighted(float snr); + /** If the packet is not already in the late rebroadcast window, move it there */ + virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; } + /** * Calculate airtime per * https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index e416160eb..997b1d6fe 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -235,12 +235,12 @@ void RadioLibInterface::onNotify(uint32_t notification) case ISR_TX: handleTransmitInterrupt(); startReceive(); - startTransmitTimer(); + setTransmitDelay(); break; case ISR_RX: handleReceiveInterrupt(); startReceive(); - startTransmitTimer(); + setTransmitDelay(); break; case TRANSMIT_DELAY_COMPLETED: @@ -250,23 +250,32 @@ void RadioLibInterface::onNotify(uint32_t notification) if (!canSendImmediately()) { setTransmitDelay(); // currently Rx/Tx-ing: reset random delay } else { - if (isChannelActive()) { // check if there is currently a LoRa packet on the channel - startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again - setTransmitDelay(); + meshtastic_MeshPacket *txp = txQueue.getFront(); + assert(txp); + long delay_remaining = txp->tx_after ? txp->tx_after - millis() : 0; + if (delay_remaining > 0) { + // There's still some delay pending on this packet, so resume waiting for it to elapse + notifyLater(delay_remaining, TRANSMIT_DELAY_COMPLETED, false); } else { - // Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and - // actual transmission as short as possible - meshtastic_MeshPacket *txp = txQueue.dequeue(); - assert(txp); - bool sent = startSend(txp); - if (sent) { - // Packet has been sent, count it toward our TX airtime utilization. - uint32_t xmitMsec = getPacketTime(txp); - airTime->logAirtime(TX_LOG, xmitMsec); + if (isChannelActive()) { // check if there is currently a LoRa packet on the channel + startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again + setTransmitDelay(); + } else { + // Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and + // actual transmission as short as possible + txp = txQueue.dequeue(); + assert(txp); + bool sent = startSend(txp); + if (sent) { + // Packet has been sent, count it toward our TX airtime utilization. + uint32_t xmitMsec = getPacketTime(txp); + airTime->logAirtime(TX_LOG, xmitMsec); + } } } } } else { + // Do nothing, because the queue is empty } break; default: @@ -277,15 +286,24 @@ void RadioLibInterface::onNotify(uint32_t notification) void RadioLibInterface::setTransmitDelay() { meshtastic_MeshPacket *p = txQueue.getFront(); + if (!p) { + return; // noop if there's nothing in the queue + } + // We want all sending/receiving to be done by our daemon thread. // We use a delay here because this packet might have been sent in response to a packet we just received. // So we want to make sure the other side has had a chance to reconfigure its radio. - /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally. - * This assumption is valid because of the offset generated by the radio to account for the noise - * floor. - */ - if (p->rx_snr == 0 && p->rx_rssi == 0) { + if (p->tx_after) { + unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec(); + unsigned long now = millis(); + p->tx_after = max(p->tx_after + add_delay, now + add_delay); + notifyLater(now - p->tx_after, TRANSMIT_DELAY_COMPLETED, false); + } else if (p->rx_snr == 0 && p->rx_rssi == 0) { + /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally. + * This assumption is valid because of the offset generated by the radio to account for the noise + * floor. + */ startTransmitTimer(true); } else { // If there is a SNR, start a timer scaled based on that SNR. @@ -312,6 +330,20 @@ void RadioLibInterface::startTransmitTimerSNR(float snr) } } +/** + * If the packet is not already in the late rebroadcast window, move it there + */ +void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id) +{ + // Look for non-late packets only, so we don't do this twice! + meshtastic_MeshPacket *p = txQueue.remove(from, id, true, false); + if (p) { + p->tx_after = millis() + getTxDelayMsecWeightedWorst(p->rx_snr); + txQueue.enqueue(p); + LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis()); + } +} + void RadioLibInterface::handleTransmitInterrupt() { // This can be null if we forced the device to enter standby mode. In that case diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index d6101ae37..dff58c9ad 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -140,10 +140,16 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified * doing the transmit */ void setTransmitDelay(); - /** random timer with certain min. and max. settings */ + /** + * random timer with certain min. and max. settings + * @return Timestamp after which the packet may be sent + */ void startTransmitTimer(bool withDelay = true); - /** timer scaled to SNR of to be flooded packet */ + /** + * timer scaled to SNR of to be flooded packet + * @return Timestamp after which the packet may be sent + */ void startTransmitTimerSNR(float snr); void handleTransmitInterrupt(); @@ -193,4 +199,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified virtual void setStandby(); const char *radioLibErr = "RadioLib err="; + + /** + * If the packet is not already in the late rebroadcast window, move it there + */ + void clampToLateRebroadcastWindow(NodeNum from, PacketId id); }; \ No newline at end of file diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index 8bc2989bf..a650ba2fc 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -18,12 +18,9 @@ static void WPA_PUT_BE16(uint8_t *a, uint16_t val) static void xor_aes_block(uint8_t *dst, const uint8_t *src) { - uint32_t *d = (uint32_t *)dst; - uint32_t *s = (uint32_t *)src; - *d++ ^= *s++; - *d++ ^= *s++; - *d++ ^= *s++; - *d++ ^= *s++; + for (uint8_t i = 0; i < AES_BLOCK_SIZE; i++) { + dst[i] ^= src[i]; + } } static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len, uint8_t *x) diff --git a/src/mesh/http/ContentHelper.h b/src/mesh/http/ContentHelper.h index a80c39f47..e5d3a2f57 100644 --- a/src/mesh/http/ContentHelper.h +++ b/src/mesh/http/ContentHelper.h @@ -1,5 +1,6 @@ #include #include +#include #define BoolToString(x) ((x) ? "true" : "false") diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index f9e5d1cc9..38aa2e2a2 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -430,4 +430,4 @@ uint8_t getWifiDisconnectReason() { return wifiDisconnectReason; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 69b2c0a38..fc3b914e5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -175,6 +175,12 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta LOG_INFO("Client set ham mode"); handleSetHamMode(r->set_ham_mode); break; + case meshtastic_AdminMessage_get_ui_config_request_tag: { + LOG_INFO("Client is getting device-ui config"); + handleGetDeviceUIConfig(mp); + handled = true; + break; + } /** * Other @@ -234,6 +240,12 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta reboot(DEFAULT_REBOOT_SECONDS); break; } + case meshtastic_AdminMessage_store_ui_config_tag: { + LOG_INFO("Storing device-ui config"); + handleStoreDeviceUIConfig(r->store_ui_config); + handled = true; + break; + } case meshtastic_AdminMessage_begin_edit_settings_tag: { LOG_INFO("Begin transaction for editing settings"); hasOpenEditTransaction = true; @@ -477,7 +489,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_REPEATER)) { config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL; - const char *warning = "Rebroadcast mode can't be set to NONE for a router or repeater\n"; + const char *warning = "Rebroadcast mode can't be set to NONE for a router or repeater"; LOG_WARN(warning); sendWarning(warning); } @@ -621,6 +633,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = false; break; + case meshtastic_Config_device_ui_tag: + // NOOP! This is handled by handleStoreDeviceUIConfig + break; } if (requiresReboot && !hasOpenEditTransaction) { disableBluetooth(); @@ -783,6 +798,10 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 LOG_INFO("Get config: Sessionkey"); res.get_config_response.which_payload_variant = meshtastic_Config_sessionkey_tag; break; + case meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG: + // NOOP! This is handled by handleGetDeviceUIConfig + res.get_config_response.which_payload_variant = meshtastic_Config_device_ui_tag; + break; } // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. // So even if we internally use 0 to represent 'use default' we still need to send the value we are @@ -995,6 +1014,14 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch } } +void AdminModule::handleGetDeviceUIConfig(const meshtastic_MeshPacket &req) +{ + meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; + r.which_payload_variant = meshtastic_AdminMessage_get_ui_config_response_tag; + r.get_ui_config_response = uiconfig; + myReply = allocDataProtobuf(r); +} + void AdminModule::reboot(int32_t seconds) { LOG_INFO("Reboot in %d seconds", seconds); @@ -1015,6 +1042,11 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot) } } +void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg) +{ + nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg); +} + void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) { // Set call sign and override lora limitations for licensed use @@ -1081,7 +1113,7 @@ bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r) r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || - r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag) + r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_response_tag) return true; else return false; @@ -1097,7 +1129,8 @@ bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r) r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag || r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag || r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag || - r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag) + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_request_tag) return true; else return false; @@ -1123,4 +1156,4 @@ void disableBluetooth() nrf52Bluetooth->shutdown(); #endif #endif -} \ No newline at end of file +} diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index b99e86707..ee2ebfd96 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -43,6 +43,7 @@ class AdminModule : public ProtobufModule, public Obser void handleGetDeviceMetadata(const meshtastic_MeshPacket &req); void handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req); void handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &req); + void handleGetDeviceUIConfig(const meshtastic_MeshPacket &req); /** * Setters */ @@ -52,6 +53,7 @@ class AdminModule : public ProtobufModule, public Obser void handleSetModuleConfig(const meshtastic_ModuleConfig &c); void handleSetChannel(); void handleSetHamMode(const meshtastic_HamParameters &req); + void handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg); void reboot(int32_t seconds); void setPassKey(meshtastic_AdminMessage *res); diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index fd9ffc9b6..a91933a0f 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -117,8 +117,10 @@ class CannedMessageModule : public SinglePortModule, public ObservableshouldDraw(); } virtual Observable *getUIFrameObservable() override { return this; } - virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; virtual bool interceptingKeyboardInput() override; +#if !HAS_TFT + virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; +#endif virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response) override; @@ -228,4 +230,4 @@ class CannedMessageModule : public SinglePortModule, public Observableinit(); #endif // INPUTBROKER_MATRIX_TYPE #endif // HAS_BUTTON -#if ARCH_PORTDUINO +#if ARCH_PORTDUINO && !HAS_TFT aLinuxInputImpl = new LinuxInputImpl(); aLinuxInputImpl->init(); #endif @@ -195,11 +195,13 @@ void setupModules() if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { new AirQualityTelemetryModule(); } +#if !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 || nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) { new HealthTelemetryModule(); } #endif +#endif #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR new PowerTelemetryModule(); #endif @@ -245,4 +247,4 @@ void setupModules() // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra // acks routingModule = new RoutingModule(); -} +} \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 92d964f7d..008da5c71 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -18,6 +18,7 @@ #include #include +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL // Sensors #include "Sensor/AHT10.h" #include "Sensor/BME280Sensor.h" @@ -36,7 +37,6 @@ #include "Sensor/SHT31Sensor.h" #include "Sensor/SHT4XSensor.h" #include "Sensor/SHTC3Sensor.h" -#include "Sensor/T1000xSensor.h" #include "Sensor/TSL2591Sensor.h" #include "Sensor/VEML7700Sensor.h" @@ -58,11 +58,12 @@ MLX90632Sensor mlx90632Sensor; DFRobotLarkSensor dfRobotLarkSensor; NAU7802Sensor nau7802Sensor; BMP3XXSensor bmp3xxSensor; +CGRadSensSensor cgRadSens; +#endif #ifdef T1000X_SENSOR_EN +#include "Sensor/T1000xSensor.h" T1000xSensor t1000xSensor; #endif -CGRadSensSensor cgRadSens; - #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -104,7 +105,7 @@ int32_t EnvironmentTelemetryModule::runOnce() // therefore, we should only enable the sensor loop if measurement is also enabled #ifdef T1000X_SENSOR_EN result = t1000xSensor.runOnce(); -#else +#elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL if (dfRobotLarkSensor.hasSensor()) result = dfRobotLarkSensor.runOnce(); if (bmp085Sensor.hasSensor()) @@ -159,8 +160,10 @@ int32_t EnvironmentTelemetryModule::runOnce() if (!moduleConfig.telemetry.environment_measurement_enabled) { return disable(); } else { +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL if (bme680Sensor.hasSensor()) result = bme680Sensor.runTrigger(); +#endif } if (((lastSentToMesh == 0) || @@ -499,6 +502,7 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule meshtastic_AdminMessage *response) { AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED; +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL if (dfRobotLarkSensor.hasSensor()) { result = dfRobotLarkSensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) @@ -609,7 +613,8 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } +#endif return result; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp index 22534e9f5..1b9b49813 100644 --- a/src/modules/Telemetry/HealthTelemetry.cpp +++ b/src/modules/Telemetry/HealthTelemetry.cpp @@ -1,6 +1,6 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO) #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "Default.h" @@ -246,4 +246,4 @@ bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) return false; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/HealthTelemetry.h b/src/modules/Telemetry/HealthTelemetry.h index fe84f2d27..01e4c2372 100644 --- a/src/modules/Telemetry/HealthTelemetry.h +++ b/src/modules/Telemetry/HealthTelemetry.h @@ -1,6 +1,6 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO) #pragma once #include "../mesh/generated/meshtastic/telemetry.pb.h" @@ -57,4 +57,4 @@ class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModu uint32_t sensor_read_error_count = 0; }; -#endif +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp index 88128a6db..f99956925 100644 --- a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp @@ -1,6 +1,6 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO) #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "MAX30102Sensor.h" @@ -80,4 +80,4 @@ bool MAX30102Sensor::getMetrics(meshtastic_Telemetry *measurement) return true; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.h b/src/modules/Telemetry/Sensor/MAX30102Sensor.h index 426d9d365..026e30ed0 100644 --- a/src/modules/Telemetry/Sensor/MAX30102Sensor.h +++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.h @@ -1,6 +1,6 @@ #include "configuration.h" -#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO) #include "../mesh/generated/meshtastic/telemetry.pb.h" #include "TelemetrySensor.h" @@ -23,4 +23,4 @@ class MAX30102Sensor : public TelemetrySensor virtual bool getMetrics(meshtastic_Telemetry *measurement) override; }; -#endif +#endif \ No newline at end of file diff --git a/src/motion/QMA6100PSensor.cpp b/src/motion/QMA6100PSensor.cpp index 4c5bc14d2..eb81e16c7 100644 --- a/src/motion/QMA6100PSensor.cpp +++ b/src/motion/QMA6100PSensor.cpp @@ -88,13 +88,13 @@ bool QMA6100PSingleton::init(ScanI2C::FoundDevice device) bool status = begin(device.address.address, &Wire); #endif if (status != true) { - LOG_WARN("QMA6100P init begin failed\n"); + LOG_WARN("QMA6100P init begin failed"); return false; } delay(20); // SW reset to make sure the device starts in a known state if (softwareReset() != true) { - LOG_WARN("QMA6100P init reset failed\n"); + LOG_WARN("QMA6100P init reset failed"); return false; } delay(20); @@ -180,4 +180,4 @@ bool QMA6100PSingleton::setWakeOnMotion() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 1a274aa28..742b295b5 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -174,6 +174,8 @@ #define HW_VENDOR meshtastic_HardwareModel_SENSECAP_INDICATOR #elif defined(SEEED_XIAO_S3) #define HW_VENDOR meshtastic_HardwareModel_SEEED_XIAO_S3 +#elif defined(MESH_TAB) +#define HW_VENDOR meshtastic_HardwareModel_MESH_TAB #endif // ----------------------------------------------------------------------------- diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 82fd8de66..4fadc3ca9 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -153,6 +153,7 @@ void portduinoSetup() std::string gpioChipName = "gpiochip"; settingsStrings[i2cdev] = ""; settingsStrings[keyboardDevice] = ""; + settingsStrings[pointerDevice] = ""; settingsStrings[webserverrootpath] = ""; settingsStrings[spidev] = ""; settingsStrings[displayspidev] = ""; @@ -455,6 +456,8 @@ bool loadConfig(const char *configPath) settingsMap[displayPanel] = ili9341; else if (yamlConfig["Display"]["Panel"].as("") == "ILI9342") settingsMap[displayPanel] = ili9342; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9486") + settingsMap[displayPanel] = ili9486; else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488") settingsMap[displayPanel] = ili9488; else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D") @@ -515,6 +518,7 @@ bool loadConfig(const char *configPath) } if (yamlConfig["Input"]) { settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as(""); + settingsStrings[pointerDevice] = (yamlConfig["Input"]["PointerDevice"]).as(""); } if (yamlConfig["Webserver"]) { @@ -570,4 +574,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} \ No newline at end of file +} diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 9cf9b6678..5bc07df6a 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -55,6 +55,7 @@ enum configNames { displayOffsetY, displayInvert, keyboardDevice, + pointerDevice, logoutputlevel, traceFilename, webserver, @@ -66,7 +67,7 @@ enum configNames { config_directory, mac_address }; -enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; +enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; enum { level_error, level_warn, level_info, level_debug, level_trace }; diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index b60d46996..b7f3f6a92 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -87,73 +87,3 @@ build_flags = -D ARDUINO_USB_MODE=0 -D ARDUINO_USB_CDC_ON_BOOT=1 -I variants/diy/t-energy-s3_e22 - -; esp32-s3 + ra-sh01 lora + 3.2" ILI9143 -[env:mesh-tab] -extends = esp32s3_base -board = um_feathers3 -board_level = extra -board_upload.flash_size = 16MB -board_build.partitions = default_16MB.csv -upload_protocol = esptool -build_flags = ${esp32s3_base.build_flags} - -D MESH_TAB - -D PRIVATE_HW - -D CONFIG_ARDUHAL_ESP_LOG - -D CONFIG_ARDUHAL_LOG_COLORS=1 - -D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks - -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 - -D MESHTASTIC_EXCLUDE_INPUTBROKER=1 - -D MESHTASTIC_EXCLUDE_BLUETOOTH=1 - -D MESHTASTIC_EXCLUDE_WEBSERVER=1 - -D LV_LVGL_H_INCLUDE_SIMPLE - -D LV_CONF_INCLUDE_SIMPLE - -D LV_COMP_CONF_INCLUDE_SIMPLE - -D LV_USE_SYSMON=0 - -D LV_USE_PROFILER=0 - -D LV_USE_PERF_MONITOR=0 - -D LV_USE_MEM_MONITOR=0 - -D LV_USE_LOG=0 - -D LV_BUILD_TEST=0 - -D USE_LOG_DEBUG - -D LOG_DEBUG_INC=\"DebugConfiguration.h\" - -D RADIOLIB_SPI_PARANOID=0 - -D MAX_NUM_NODES=250 - -D MAX_THREADS=40 - -D HAS_SCREEN=0 - -D HAS_TFT=1 - -D RAM_SIZE=1024 - -D LGFX_DRIVER_TEMPLATE - -D LGFX_DRIVER=LGFX_GENERIC - -D LGFX_PANEL=ILI9341 - -D LGFX_OFFSET_ROTATION=1 - -D LGFX_TOUCH=XPT2046 - -D LGFX_PIN_SCK=12 - -D LGFX_PIN_MOSI=13 - -D LGFX_PIN_MISO=11 - -D LGFX_PIN_DC=16 - -D LGFX_PIN_CS=10 - -D LGFX_PIN_RST=-1 - -D LGFX_PIN_BL=42 - -D LGFX_TOUCH_INT=41 - -D LGFX_TOUCH_CS=7 - -D LGFX_TOUCH_CLK=12 - -D LGFX_TOUCH_DO=11 - -D LGFX_TOUCH_DIN=13 - -D LGFX_TOUCH_X_MIN=300 - -D LGFX_TOUCH_X_MAX=3900 - -D LGFX_TOUCH_Y_MIN=400 - -D LGFX_TOUCH_Y_MAX=3900 - -D VIEW_320x240 - -D USE_PACKET_API - -I lib/device-ui/generated/ui_320x240 - -I variants/diy/mesh-tab -build_src_filter = ${esp32_base.build_src_filter} - +<../lib/device-ui/generated/ui_320x240> - +<../lib/device-ui/resources> - +<../lib/device-ui/locale> - +<../lib/device-ui/source> -lib_deps = ${esp32_base.lib_deps} - lovyan03/LovyanGFX@^1.1.16 - earlephilhower/ESP8266Audio@^1.9.7 - earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file diff --git a/variants/mesh-tab/pins_arduino.h b/variants/mesh-tab/pins_arduino.h new file mode 100644 index 000000000..c995f638c --- /dev/null +++ b/variants/mesh-tab/pins_arduino.h @@ -0,0 +1,65 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "soc/soc_caps.h" +#include + +#define USB_VID 0x303A +#define USB_PID 0x80D6 + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 5; +static const uint8_t MOSI = 35; +static const uint8_t MISO = 37; +static const uint8_t SDO = 35; +static const uint8_t SDI = 37; +static const uint8_t SCK = 36; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; + +static const uint8_t T1 = 1; +static const uint8_t T3 = 3; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T14 = 14; + +static const uint8_t VBAT_SENSE = 2; +static const uint8_t VBUS_SENSE = 34; + +// User LED +#define LED_BUILTIN 13 +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t RGB_DATA = 40; +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API neopixelWrite() +#define RGB_BUILTIN (RGB_DATA + SOC_GPIO_PIN_COUNT) +#define RGB_BRIGHTNESS 64 + +static const uint8_t RGB_PWR = 39; +static const uint8_t LDO2 = 39; +static const uint8_t LED = 13; + +#endif /* Pins_Arduino_h */ diff --git a/variants/mesh-tab/platformio.ini b/variants/mesh-tab/platformio.ini new file mode 100644 index 000000000..26b072cde --- /dev/null +++ b/variants/mesh-tab/platformio.ini @@ -0,0 +1,233 @@ +; Base for Mesh-Tab device (esp32-s3 N16R2 + ra-sh01 lora) +; https://github.com/valzzu/Mesh-Tab +[mesh_tab_base] +extends = esp32s3_base +board = mesh-tab +board_level = extra +board_upload.flash_size = 16MB +board_build.partitions = default_16MB.csv +upload_protocol = esptool +build_flags = ${esp32s3_base.build_flags} + -D MESH_TAB + -D CONFIG_ARDUHAL_ESP_LOG + -D CONFIG_ARDUHAL_LOG_COLORS=1 + -D CONFIG_DISABLE_HAL_LOCKS=1 + -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 + -D MESHTASTIC_EXCLUDE_INPUTBROKER=1 + -D MESHTASTIC_EXCLUDE_BLUETOOTH=1 + -D MESHTASTIC_EXCLUDE_WEBSERVER=1 + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_USE_SYSMON=0 + -D LV_USE_PROFILER=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D LV_USE_LOG=0 + -D LV_BUILD_TEST=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" + -D RADIOLIB_SPI_PARANOID=0 + -D MAX_NUM_NODES=250 + -D MAX_THREADS=40 + -D HAS_SCREEN=0 + -D HAS_TFT=1 + -D USE_PIN_BUZZER + -D RAM_SIZE=1024 + -D LGFX_DRIVER_TEMPLATE + -D LGFX_DRIVER=LGFX_GENERIC + -D LGFX_PIN_SCK=12 + -D LGFX_PIN_MOSI=13 + -D LGFX_PIN_MISO=11 + -D LGFX_PIN_DC=16 + -D LGFX_PIN_CS=10 + -D LGFX_PIN_RST=-1 + -D LGFX_PIN_BL=42 + -D LGFX_TOUCH_INT=41 + -D VIEW_320x240 + -D USE_PACKET_API + -I lib/device-ui/generated/ui_320x240 + -I variants/mesh-tab +build_src_filter = ${esp32_base.build_src_filter} + +<../lib/device-ui/generated/ui_320x240> + +<../lib/device-ui/resources> + +<../lib/device-ui/locale> + +<../lib/device-ui/source> +lib_deps = ${esp32_base.lib_deps} + lovyan03/LovyanGFX@^1.1.16 + +; 3.2" TN TFT ST7789 / XPT2046: https://vi.aliexpress.com/item/1005005933490544.html +[env:mesh-tab-3-2-TN-resistive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D LGFX_SCREEN_WIDTH=240 + -D LGFX_SCREEN_HEIGHT=320 + -D LGFX_PANEL=ST7789 + -D LGFX_INVERT_COLOR=false + -D LGFX_RGB_ORDER=false + -D LGFX_ROTATION=3 + -D LGFX_TOUCH=XPT2046 + -D SPI_FREQUENCY=60000000 + -D LGFX_TOUCH_SPI_FREQ=2500000 + -D LGFX_TOUCH_SPI_HOST=2 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D LGFX_TOUCH_ROTATION=4 + +; 3.2" IPS TFT ILI9341 / XPT2046: https://www.aliexpress.com/item/1005006258575617.html +[env:mesh-tab-3-2-IPS-resistive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D LGFX_SCREEN_WIDTH=240 + -D LGFX_SCREEN_HEIGHT=320 + -D LGFX_PANEL=ILI9341 + -D LGFX_INVERT_COLOR=true + -D LGFX_RGB_ORDER=false + -D LGFX_ROTATION=1 + -D LGFX_TOUCH=XPT2046 + -D SPI_FREQUENCY=60000000 ; if image is distorted then lower to 40 MHz + -D LGFX_TOUCH_SPI_FREQ=2500000 + -D LGFX_TOUCH_SPI_HOST=2 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D LGFX_TOUCH_ROTATION=4 + +; 3.5" IPS TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/1005006333922639.html +[env:mesh-tab-3-5-IPS-resistive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D DISPLAY_SET_RESOLUTION + -D LGFX_SCREEN_WIDTH=320 + -D LGFX_SCREEN_HEIGHT=480 + -D LGFX_PANEL=ILI9488 + -D LGFX_INVERT_COLOR=true + -D LGFX_RGB_ORDER=false + -D LGFX_DLEN_16BITS=false + -D LGFX_ROTATION=0 + -D LGFX_TOUCH=XPT2046 + -D SPI_FREQUENCY=40000000 ; may go higher upto 40/60/80 MHz + -D LGFX_TOUCH_SPI_FREQ=2500000 + -D LGFX_TOUCH_SPI_HOST=2 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D LGFX_TOUCH_ROTATION=0 + +; 3.5" TN TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/32985467436.html +[env:mesh-tab-3-5-TN-resistive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D DISPLAY_SET_RESOLUTION + -D LGFX_SCREEN_WIDTH=320 + -D LGFX_SCREEN_HEIGHT=480 + -D LGFX_PANEL=HX8357B + -D SPI_FREQUENCY=60000000 + -D LGFX_INVERT_COLOR=false + -D LGFX_RGB_ORDER=false + -D LGFX_DLEN_16BITS=false + -D LGFX_ROTATION=4 + -D LGFX_TOUCH=XPT2046 + -D LGFX_TOUCH_SPI_FREQ=2500000 + -D LGFX_TOUCH_SPI_HOST=2 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D LGFX_TOUCH_ROTATION=2 + +; 3.2" IPS TFT ILI9341 / FT6236: https://vi.aliexpress.com/item/1005006624072350.html +[env:mesh-tab-3-2-IPS-capacitive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D LGFX_SCREEN_WIDTH=240 + -D LGFX_SCREEN_HEIGHT=320 + -D LGFX_PANEL=ILI9341 + -D LGFX_INVERT_COLOR=true + -D LGFX_RGB_ORDER=false + -D LGFX_ROTATION=1 + -D LGFX_TOUCH=FT5x06 + -D SPI_FREQUENCY=40000000 ; may go higher upto 60/80 MHz + -D LGFX_TOUCH_I2C_PORT=0 + -D LGFX_TOUCH_I2C_ADDR=0x38 + -D LGFX_TOUCH_I2C_SDA=8 + -D LGFX_TOUCH_I2C_SCL=9 + -D LGFX_TOUCH_RST=7 + -D LGFX_TOUCH_X_MIN=0 + -D LGFX_TOUCH_X_MAX=319 + -D LGFX_TOUCH_Y_MIN=0 + -D LGFX_TOUCH_Y_MAX=479 + -D LGFX_TOUCH_ROTATION=0 + -D LGFX_TOUCH_I2C_FREQ=1000000 + +; 3.5" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005006893699919.html +[env:mesh-tab-3-5-IPS-capacitive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D DISPLAY_SET_RESOLUTION + -D LGFX_SCREEN_WIDTH=320 + -D LGFX_SCREEN_HEIGHT=480 + -D LGFX_PANEL=ILI9488 + -D LGFX_INVERT_COLOR=true + -D LGFX_RGB_ORDER=false + -D LGFX_DLEN_16BITS=false + -D LGFX_ROTATION=1 + -D LGFX_TOUCH=FT5x06 + -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz + -D LGFX_TOUCH_I2C_PORT=0 + -D LGFX_TOUCH_I2C_ADDR=0x38 + -D LGFX_TOUCH_I2C_SDA=8 + -D LGFX_TOUCH_I2C_SCL=9 + -D LGFX_TOUCH_RST=7 + -D LGFX_TOUCH_X_MIN=0 + -D LGFX_TOUCH_X_MAX=319 + -D LGFX_TOUCH_Y_MIN=0 + -D LGFX_TOUCH_Y_MAX=479 + -D LGFX_TOUCH_ROTATION=1 + -D LGFX_TOUCH_I2C_FREQ=1000000 + +; 4.0" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005007082906950.html +[env:mesh-tab-4-0-IPS-capacitive] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D DISPLAY_SET_RESOLUTION + -D LGFX_SCREEN_WIDTH=320 + -D LGFX_SCREEN_HEIGHT=480 + -D LGFX_PANEL=HX8357B + -D LGFX_INVERT_COLOR=true + -D LGFX_RGB_ORDER=false + -D LGFX_DLEN_16BITS=false + -D LGFX_ROTATION=4 + -D LGFX_TOUCH=FT5x06 + -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz + -D LGFX_TOUCH_I2C_PORT=0 + -D LGFX_TOUCH_I2C_ADDR=0x38 + -D LGFX_TOUCH_I2C_SDA=8 + -D LGFX_TOUCH_I2C_SCL=9 + -D LGFX_TOUCH_RST=7 + -D LGFX_TOUCH_X_MIN=0 + -D LGFX_TOUCH_X_MAX=319 + -D LGFX_TOUCH_Y_MIN=0 + -D LGFX_TOUCH_Y_MAX=479 + -D LGFX_TOUCH_ROTATION=1 + -D LGFX_TOUCH_I2C_FREQ=1000000 \ No newline at end of file diff --git a/variants/diy/mesh-tab/variant.h b/variants/mesh-tab/variant.h similarity index 67% rename from variants/diy/mesh-tab/variant.h rename to variants/mesh-tab/variant.h index 0a23a3c36..533c931bc 100644 --- a/variants/diy/mesh-tab/variant.h +++ b/variants/mesh-tab/variant.h @@ -7,8 +7,8 @@ // Analog pins #define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage -// ratio of voltage divider = 2.0 -#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage. +// ratio of voltage divider (100k, 220k) +#define ADC_MULTIPLIER 1.6 // 1.45 + 10% for correction of display undervoltage. #define ADC_CHANNEL ADC1_GPIO4_CHANNEL // LED @@ -17,6 +17,9 @@ // Button #define BUTTON_PIN 0 +// Button +#define PIN_BUZZER 5 + // GPS #define GPS_RX_PIN 18 #define GPS_TX_PIN 17 @@ -28,20 +31,26 @@ #define SPI_CS 10 #define SDCARD_CS 6 +// LORA MODULES +#define USE_SX1262 + // LORA SPI #define LORA_SCK 36 #define LORA_MISO 37 #define LORA_MOSI 35 #define LORA_CS 39 -// LORA MODULES -#define USE_SX1262 - // LORA CONFIG +#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_RESET 14 +#define LORA_DIO1 15 // SX1262 IRQ +#define LORA_DIO2 40 // SX1262 BUSY +#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262 + #define SX126X_CS LORA_CS -#define SX126X_DIO1 15 +#define SX126X_DIO1 LORA_DIO1 #define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_BUSY 40 +#define SX126X_BUSY LORA_DIO2 #define SX126X_RESET 14 #define SX126X_RXEN 47 #define SX126X_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin diff --git a/variants/rak2560/RAK9154Sensor.cpp b/variants/rak2560/RAK9154Sensor.cpp index 9f660947e..43affe581 100644 --- a/variants/rak2560/RAK9154Sensor.cpp +++ b/variants/rak2560/RAK9154Sensor.cpp @@ -37,11 +37,11 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT break; case SNHUBAPI_EVT_ADD_SID: - // LOG_INFO("+ADD:SID:[%02x]\r\n", msg[0]); + // LOG_INFO("+ADD:SID:[%02x]", msg[0]); break; case SNHUBAPI_EVT_ADD_PID: - // LOG_INFO("+ADD:PID:[%02x]\r\n", msg[0]); + // LOG_INFO("+ADD:PID:[%02x]", msg[0]); #ifdef BOOT_DATA_REQ provision = msg[0]; #endif @@ -55,12 +55,12 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT case SNHUBAPI_EVT_SDATA_REQ: - // LOG_INFO("+EVT:PID[%02x],IPSO[%02x]\r\n",pid,msg[0]); + // LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]); // for( uint16_t i=1; i 0) ? true : false; } -#endif // HAS_RAKPROT \ No newline at end of file +#endif // HAS_RAKPROT diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini index 075811610..0bce9fbb5 100644 --- a/variants/tracker-t1000-e/platformio.ini +++ b/variants/tracker-t1000-e/platformio.ini @@ -4,6 +4,7 @@ board = tracker-t1000-e build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE + -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL=1 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/tracker-t1000-e> lib_deps = @@ -11,4 +12,4 @@ lib_deps = https://github.com/meshtastic/QMA6100P_Arduino_Library.git#14c900b8b2e4feaac5007a7e41e0c1b7f0841136 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -upload_protocol = nrfutil +upload_protocol = nrfutil \ No newline at end of file diff --git a/variants/unphone/pins_arduino.h b/variants/unphone/pins_arduino.h index c4e9add1c..74067359f 100644 --- a/variants/unphone/pins_arduino.h +++ b/variants/unphone/pins_arduino.h @@ -6,14 +6,6 @@ #define USB_VID 0x16D0 #define USB_PID 0x1178 -#define EXTERNAL_NUM_INTERRUPTS 46 -#define NUM_DIGITAL_PINS 48 -#define NUM_ANALOG_INPUTS 20 - -#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) -#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) -#define digitalPinHasPWM(p) (p < 46) - #define LED_BUILTIN 13 #define BUILTIN_LED LED_BUILTIN // backward compatibility