From 3d6a891586b9a3e02d8c69287a2913adaf1bd914 Mon Sep 17 00:00:00 2001 From: Hacuchino-hash <246103064+Hacuchino-hash@users.noreply.github.com> Date: Tue, 4 Aug 2026 12:59:16 -0500 Subject: [PATCH 1/5] Add support for Seeed SenseCAP MeshTracker X1 (LR2021) --- boards/seeed-mesh-tracker-x1.json | 60 +++++++++ .../meshtracker_x1/MeshTrackerX1Board.cpp | 23 ++++ variants/meshtracker_x1/MeshTrackerX1Board.h | 87 +++++++++++++ variants/meshtracker_x1/platformio.ini | 117 +++++++++++++++++ variants/meshtracker_x1/target.cpp | 116 +++++++++++++++++ variants/meshtracker_x1/target.h | 44 +++++++ variants/meshtracker_x1/variant.cpp | 106 +++++++++++++++ variants/meshtracker_x1/variant.h | 123 ++++++++++++++++++ 8 files changed, 676 insertions(+) create mode 100644 boards/seeed-mesh-tracker-x1.json create mode 100644 variants/meshtracker_x1/MeshTrackerX1Board.cpp create mode 100644 variants/meshtracker_x1/MeshTrackerX1Board.h create mode 100644 variants/meshtracker_x1/platformio.ini create mode 100644 variants/meshtracker_x1/target.cpp create mode 100644 variants/meshtracker_x1/target.h create mode 100644 variants/meshtracker_x1/variant.cpp create mode 100644 variants/meshtracker_x1/variant.h diff --git a/boards/seeed-mesh-tracker-x1.json b/boards/seeed-mesh-tracker-x1.json new file mode 100644 index 00000000..347aca53 --- /dev/null +++ b/boards/seeed-mesh-tracker-x1.json @@ -0,0 +1,60 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v7.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"], + ["0x2886", "0x0057"] + ], + "usb_product": "X1-BOOT", + "mcu": "nrf52840", + "variant": "Seeed_Mesh-Tracker-X1", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "7.3.0", + "sd_fwid": "0x0123" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd", + "openocd_target": "nrf52.cfg" + }, + "frameworks": ["arduino"], + "name": "Seeed SenseCAP MeshTracker X1", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink", + "cmsis-dap", + "blackmagic" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://www.seeedstudio.com/SenseCAP-MeshTracker-X1-for-Meshtastic-p-6793.html", + "vendor": "Seeed Studio" +} diff --git a/variants/meshtracker_x1/MeshTrackerX1Board.cpp b/variants/meshtracker_x1/MeshTrackerX1Board.cpp new file mode 100644 index 00000000..56e537a2 --- /dev/null +++ b/variants/meshtracker_x1/MeshTrackerX1Board.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include "MeshTrackerX1Board.h" + +void MeshTrackerX1Board::begin() { + NRF52BoardDCDC::begin(); + btn_prev_state = LOW; // button is active HIGH + +#ifdef BUTTON_PIN + pinMode(BATTERY_PIN, INPUT); + pinMode(BUTTON_PIN, INPUT_PULLDOWN); + pinMode(LED_PIN, OUTPUT); +#endif + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#endif + + Wire.begin(); + + delay(10); // give lr2021 some time to power up +} diff --git a/variants/meshtracker_x1/MeshTrackerX1Board.h b/variants/meshtracker_x1/MeshTrackerX1Board.h new file mode 100644 index 00000000..51f022e0 --- /dev/null +++ b/variants/meshtracker_x1/MeshTrackerX1Board.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include + +class MeshTrackerX1Board : public NRF52BoardDCDC { +protected: + uint8_t btn_prev_state; + +public: + MeshTrackerX1Board() : NRF52Board("X1_OTA") {} + void begin(); + + uint16_t getBattMilliVolts() override { + #ifdef BATTERY_PIN + #ifdef PIN_BAT_ADC_EN + digitalWrite(PIN_BAT_ADC_EN, HIGH); + #endif + analogReference(AR_INTERNAL_3_0); + analogReadResolution(12); + delay(10); + float volts = (analogRead(BATTERY_PIN) * ADC_MULTIPLIER * AREF_VOLTAGE) / 4096; + + analogReference(AR_DEFAULT); // put back to default + analogReadResolution(10); + + return volts * 1000; + #else + return 0; + #endif + } + + const char* getManufacturerName() const override { + return "Seeed SenseCAP MeshTracker X1"; + } + + int buttonStateChanged() { + #ifdef BUTTON_PIN + uint8_t v = digitalRead(BUTTON_PIN); + if (v != btn_prev_state) { + btn_prev_state = v; + return (v == USER_BTN_PRESSED) ? 1 : -1; + } + #endif + return 0; + } + + void powerOff() override { + #ifdef HAS_GPS + digitalWrite(GPS_VRTC_EN, LOW); + digitalWrite(GPS_RESET, LOW); + digitalWrite(GPS_SLEEP_INT, LOW); + digitalWrite(GPS_RTC_INT, LOW); + digitalWrite(GPS_EN, LOW); + #endif + + #ifdef PIN_DRV_EN + digitalWrite(PIN_DRV_EN, LOW); + #endif + + #ifdef PIN_BAT_ADC_EN + digitalWrite(PIN_BAT_ADC_EN, LOW); + #endif + + #ifdef PIN_3V3_EN + digitalWrite(PIN_3V3_EN, LOW); + #endif + + // set led on and wait for button release before poweroff + #ifdef LED_PIN + digitalWrite(LED_PIN, HIGH); + #endif + #ifdef BUTTON_PIN + while(digitalRead(BUTTON_PIN)); + #endif + #ifdef LED_PIN + digitalWrite(LED_PIN, LOW); + #endif + + #ifdef BUTTON_PIN + nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH); + #endif + + sd_power_system_off(); + } +}; diff --git a/variants/meshtracker_x1/platformio.ini b/variants/meshtracker_x1/platformio.ini new file mode 100644 index 00000000..dbab5ca6 --- /dev/null +++ b/variants/meshtracker_x1/platformio.ini @@ -0,0 +1,117 @@ +[MeshTracker_X1] +extends = nrf52_base +board = seeed-mesh-tracker-x1 +board_build.ldscript = boards/nrf52840_s140_v7.ld +build_flags = ${nrf52_base.build_flags} + -I src/helpers/nrf52 + -I lib/nrf52/s140_nrf52_7.3.0_API/include + -I lib/nrf52/s140_nrf52_7.3.0_API/include/nrf52 + -I variants/meshtracker_x1 + -I src/helpers/ui + -D MESH_TRACKER_X1 + -D PIN_USER_BTN=6 + -D USER_BTN_PRESSED=HIGH + -D PIN_STATUS_LED=24 + -D RADIO_CLASS=CustomLR2021 + -D USE_LR2021 + -D WRAPPER_CLASS=CustomLR2021Wrapper + -D LORA_TX_POWER=22 + -D P_LORA_BUSY=7 ; P0.7 + -D P_LORA_SCLK=11 ; P0.11 + -D P_LORA_NSS=12 ; P0.12 + -D P_LORA_DIO_1=33 ; P1.1 + -D P_LORA_MISO=40 ; P1.8 + -D P_LORA_MOSI=41 ; P1.9 + -D P_LORA_RESET=42 ; P1.10 + -D LR2021_IRQ_DIO=8 ; P1.1 is connected to LR2021 DIO8 + -D LR2021_TCXO_VOLTAGE=1.6f + -D ENV_INCLUDE_GPS=1 +build_src_filter = ${nrf52_base.build_src_filter} + + + +<../variants/meshtracker_x1> +debug_tool = jlink +upload_protocol = nrfutil + +[env:MeshTracker_X1_repeater] +extends = MeshTracker_X1 +build_flags = ${MeshTracker_X1.build_flags} + -I examples/companion_radio/ui-orig + -D ADVERT_NAME='"MeshTracker X1 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${MeshTracker_X1.build_src_filter} + +<../examples/simple_repeater> +lib_deps = ${MeshTracker_X1.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 + +[env:MeshTracker_X1_room_server] +extends = MeshTracker_X1 +build_flags = ${MeshTracker_X1.build_flags} + -I examples/companion_radio/ui-orig + -D ADVERT_NAME='"MeshTracker X1 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${MeshTracker_X1.build_src_filter} + +<../examples/simple_room_server> +lib_deps = ${MeshTracker_X1.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 + +[env:MeshTracker_X1_companion_radio_usb] +extends = MeshTracker_X1 +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld +board_upload.maximum_size = 708608 +build_flags = ${MeshTracker_X1.build_flags} + -I examples/companion_radio/ui-orig + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D OFFLINE_QUEUE_SIZE=256 + -D DISPLAY_CLASS=NullDisplayDriver + -D PIN_BUZZER=25 + -D ENABLE_USB_INTERFACE +build_src_filter = ${MeshTracker_X1.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = ${MeshTracker_X1.lib_deps} + densaugeo/base64 @ ~1.4.0 + stevemarple/MicroNMEA @ ^2.0.6 + end2endzone/NonBlockingRTTTL@^1.3.0 + +[env:MeshTracker_X1_companion_radio_ble] +extends = MeshTracker_X1 +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld +board_upload.maximum_size = 708608 +build_flags = ${MeshTracker_X1.build_flags} + -I examples/companion_radio/ui-orig + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_TX_POWER=0 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D OFFLINE_QUEUE_SIZE=256 + -D DISPLAY_CLASS=NullDisplayDriver + -D PIN_BUZZER=25 + -D ADVERT_NAME='"@@MAC"' +build_src_filter = ${MeshTracker_X1.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-orig/*.cpp> +lib_deps = ${MeshTracker_X1.lib_deps} + densaugeo/base64 @ ~1.4.0 + stevemarple/MicroNMEA @ ^2.0.6 + end2endzone/NonBlockingRTTTL@^1.3.0 diff --git a/variants/meshtracker_x1/target.cpp b/variants/meshtracker_x1/target.cpp new file mode 100644 index 00000000..1139b8b7 --- /dev/null +++ b/variants/meshtracker_x1/target.cpp @@ -0,0 +1,116 @@ +#include +#include "target.h" +#include + +MeshTrackerX1Board board; + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock rtc_clock; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); +MeshTrackerX1SensorManager sensors = MeshTrackerX1SensorManager(nmea); + +#ifdef DISPLAY_CLASS + NullDisplayDriver display; +#endif + +bool radio_init() { + return radio.std_init(&SPI); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} + +void MeshTrackerX1SensorManager::start_gps() { + gps_active = true; + // this init sequence comes from seeed examples and deals with all gps pins + pinMode(GPS_EN, OUTPUT); + digitalWrite(GPS_EN, HIGH); + delay(10); + pinMode(GPS_VRTC_EN, OUTPUT); + digitalWrite(GPS_VRTC_EN, HIGH); + delay(10); + + pinMode(GPS_RESET, OUTPUT); + digitalWrite(GPS_RESET, HIGH); + delay(10); + digitalWrite(GPS_RESET, LOW); + + pinMode(GPS_SLEEP_INT, OUTPUT); + digitalWrite(GPS_SLEEP_INT, HIGH); + pinMode(GPS_RTC_INT, OUTPUT); + digitalWrite(GPS_RTC_INT, LOW); +} + +void MeshTrackerX1SensorManager::sleep_gps() { + gps_active = false; + digitalWrite(GPS_VRTC_EN, HIGH); // keep RTC alive for faster fix on wake + digitalWrite(GPS_EN, LOW); + digitalWrite(GPS_RESET, LOW); + digitalWrite(GPS_SLEEP_INT, HIGH); + digitalWrite(GPS_RTC_INT, LOW); +} + +void MeshTrackerX1SensorManager::stop_gps() { + gps_active = false; + digitalWrite(GPS_VRTC_EN, LOW); + digitalWrite(GPS_EN, LOW); + digitalWrite(GPS_RESET, LOW); + digitalWrite(GPS_SLEEP_INT, HIGH); + digitalWrite(GPS_RTC_INT, LOW); +} + +bool MeshTrackerX1SensorManager::begin() { + // init GPS + Serial1.begin(GPS_BAUD_RATE); + return true; +} + +bool MeshTrackerX1SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void MeshTrackerX1SensorManager::loop() { + static long next_gps_update = 0; + + _nmea->loop(); + + if (millis() > next_gps_update) { + if (gps_active && _nmea->isValid()) { + node_lat = ((double)_nmea->getLatitude())/1000000.; + node_lon = ((double)_nmea->getLongitude())/1000000.; + node_altitude = ((double)_nmea->getAltitude()) / 1000.0; + } + next_gps_update = millis() + 1000; + } +} + +int MeshTrackerX1SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) + +const char* MeshTrackerX1SensorManager::getSettingName(int i) const { + return i == 0 ? "gps" : NULL; +} +const char* MeshTrackerX1SensorManager::getSettingValue(int i) const { + if (i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} +bool MeshTrackerX1SensorManager::setSettingValue(const char* name, const char* value) { + if (strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + sleep_gps(); // sleep for faster fix ! + } else { + start_gps(); + } + return true; + } + return false; // not supported +} diff --git a/variants/meshtracker_x1/target.h b/variants/meshtracker_x1/target.h new file mode 100644 index 00000000..665f4328 --- /dev/null +++ b/variants/meshtracker_x1/target.h @@ -0,0 +1,44 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include "MeshTrackerX1Board.h" +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include "NullDisplayDriver.h" +#endif + +class MeshTrackerX1SensorManager: public SensorManager { + bool gps_active = false; + LocationProvider * _nmea; + + void start_gps(); + void sleep_gps(); + void stop_gps(); +public: + MeshTrackerX1SensorManager(LocationProvider &nmea): _nmea(&nmea) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; + LocationProvider* getLocationProvider() { return _nmea; } +}; + +#ifdef DISPLAY_CLASS + extern NullDisplayDriver display; +#endif + +extern MeshTrackerX1Board board; +extern WRAPPER_CLASS radio_driver; +extern VolatileRTCClock rtc_clock; +extern MeshTrackerX1SensorManager sensors; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/meshtracker_x1/variant.cpp b/variants/meshtracker_x1/variant.cpp new file mode 100644 index 00000000..a0e64380 --- /dev/null +++ b/variants/meshtracker_x1/variant.cpp @@ -0,0 +1,106 @@ +/* + * variant.cpp - Seeed SenseCAP MeshTracker X1 (nRF52840 + Semtech LR2021) + */ + +#include "variant.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] = +{ + 0, // P0.00 + 1, // P0.01 + 2, // P0.02, AIN0 BATTERY_PIN + 3, // P0.03, LED_RED + 4, // P0.04, AIN2 VCC_ADC + 5, // P0.05, AIN3 EXT_PWR_DETECT + 6, // P0.06, PIN_BUTTON1 + 7, // P0.07, LORA_BUSY + 8, // P0.08, GPS_RESET + 9, // P0.09, PIN_RTC_EN + 10, // P0.10 + 11, // P0.11, PIN_SPI_SCK + 12, // P0.12, PIN_SPI_NSS + 13, // P0.13, PIN_SERIAL1_TX + 14, // P0.14, PIN_SERIAL1_RX + 15, // P0.15 + 16, // P0.16, PIN_SERIAL2_TX + 17, // P0.17, PIN_SERIAL2_RX + 18, // P0.18 + 19, // P0.19 + 20, // P0.20 + 21, // P0.21 + 22, // P0.22 + 23, // P0.23 + 24, // P0.24, LED_GREEN + 25, // P0.25, BUZZER_PIN + 26, // P0.26 + 27, // P0.27 + 28, // P0.28, LED_BLUE + 29, // P0.29, GPS_RTC_INT + 30, // P0.30, GPS_SLEEP_INT + 31, // P0.31 + 32, // P1.00 + 33, // P1.01, LORA_DIO_1 (LR2021 DIO8 IRQ) + 34, // P1.02 + 35, // P1.03, EXT_CHRG_DETECT + 36, // P1.04, CHARGE_DONE + 37, // P1.05, PIN_DRV_EN + 38, // P1.06, PIN_BAT_ADC_EN + 39, // P1.07, PIN_3V3_EN + 40, // P1.08, PIN_SPI_MISO + 41, // P1.09, PIN_SPI_MOSI + 42, // P1.10, LORA_RESET + 43, // P1.11, GPS_EN + 44, // P1.12 + 45, // P1.13, GPS_VRTC_EN + 46, // P1.14, PIN_WIRE_SCL + 47, // P1.15, PIN_WIRE_SDA + 255, // NRFX_SPIM_PIN_NOT_USED +}; + +void initVariant() +{ + pinMode(BATTERY_PIN, INPUT); + pinMode(EXT_CHRG_DETECT, INPUT); + pinMode(EXT_PWR_DETECT, INPUT); + pinMode(PIN_BUTTON1, INPUT_PULLDOWN); + + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); + + pinMode(PIN_BAT_ADC_EN, OUTPUT); + digitalWrite(PIN_BAT_ADC_EN, HIGH); + + pinMode(PIN_RTC_EN, OUTPUT); + digitalWrite(PIN_RTC_EN, LOW); + + pinMode(PIN_DRV_EN, OUTPUT); + digitalWrite(PIN_DRV_EN, LOW); + + pinMode(LED_RED, OUTPUT); + digitalWrite(LED_RED, LOW); + + pinMode(LED_GREEN, OUTPUT); + digitalWrite(LED_GREEN, LOW); + + pinMode(LED_BLUE, OUTPUT); + digitalWrite(LED_BLUE, LOW); + + pinMode(GPS_EN, OUTPUT); + digitalWrite(GPS_EN, LOW); + + pinMode(GPS_VRTC_EN, OUTPUT); + digitalWrite(GPS_VRTC_EN, HIGH); + + pinMode(GPS_RESET, OUTPUT); + digitalWrite(GPS_RESET, LOW); + + pinMode(GPS_SLEEP_INT, OUTPUT); + digitalWrite(GPS_SLEEP_INT, HIGH); + + pinMode(GPS_RTC_INT, OUTPUT); + digitalWrite(GPS_RTC_INT, LOW); + + pinMode(BUZZER_PIN, INPUT_PULLDOWN); +} diff --git a/variants/meshtracker_x1/variant.h b/variants/meshtracker_x1/variant.h new file mode 100644 index 00000000..8e9bae8f --- /dev/null +++ b/variants/meshtracker_x1/variant.h @@ -0,0 +1,123 @@ +/* + * variant.h - Seeed SenseCAP MeshTracker X1 (nRF52840 + Semtech LR2021) + */ + +#pragma once + +#include "WVariant.h" + +//////////////////////////////////////////////////////////////////////////////// +// Low frequency clock source + +#define USE_LFXO // 32.768 kHz crystal oscillator +#define VARIANT_MCK (64000000ul) + +//////////////////////////////////////////////////////////////////////////////// +// Power + +#define NRF_APM // detect usb power +#define PIN_3V3_EN (39) // P1.7 Power to sensors +#define PIN_BAT_ADC_EN (38) // P1.6 Power to battery ADC divider +#define PIN_RTC_EN (9) // P0.9 Power to RTC + +#define BATTERY_PIN (2) // P0.2/AIN0 +#define BATTERY_IMMUTABLE +#define ADC_MULTIPLIER (2.0F) + +#define EXT_CHRG_DETECT (35) // P1.3, LOW while charging +#define EXT_PWR_DETECT (5) // P0.5 + +#define ADC_RESOLUTION (14) +#define BATTERY_SENSE_RES (12) + +#define AREF_VOLTAGE (3.0) + +//////////////////////////////////////////////////////////////////////////////// +// Number of pins + +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +//////////////////////////////////////////////////////////////////////////////// +// UART pin definition + +#define PIN_SERIAL1_RX (14) // P0.14 (GPS) +#define PIN_SERIAL1_TX (13) // P0.13 (GPS) + +#define PIN_SERIAL2_RX (17) // P0.17 +#define PIN_SERIAL2_TX (16) // P0.16 + +//////////////////////////////////////////////////////////////////////////////// +// I2C pin definition + +#define HAS_WIRE (1) +#define WIRE_INTERFACES_COUNT (1) + +#define PIN_WIRE_SDA (47) // P1.15 +#define PIN_WIRE_SCL (46) // P1.14 +#define I2C_NO_RESCAN + +//////////////////////////////////////////////////////////////////////////////// +// SPI pin definition + +#define SPI_INTERFACES_COUNT (1) + +#define PIN_SPI_MISO (40) // P1.8 +#define PIN_SPI_MOSI (41) // P1.9 +#define PIN_SPI_SCK (11) // P0.11 +#define PIN_SPI_NSS (12) // P0.12 + +//////////////////////////////////////////////////////////////////////////////// +// Builtin LEDs (RGB) + +#define LED_BUILTIN (-1) +#define LED_RED (3) // P0.3 +#define LED_GREEN (24) // P0.24 +#define LED_BLUE (28) // P0.28 +#define LED_PIN LED_GREEN + +#define LED_STATE_ON HIGH + +//////////////////////////////////////////////////////////////////////////////// +// Builtin buttons + +#define PIN_BUTTON1 (6) // P0.6, active HIGH (internal pull-down) +#define BUTTON_PIN PIN_BUTTON1 + +//////////////////////////////////////////////////////////////////////////////// +// LR2021 + +#define LORA_DIO_1 (33) // P1.1, IRQ line (LR2021 DIO8) +#define LORA_NSS (PIN_SPI_NSS) // P0.12 +#define LORA_RESET (42) // P1.10 +#define LORA_BUSY (7) // P0.7 +#define LORA_SCLK (PIN_SPI_SCK) // P0.11 +#define LORA_MISO (PIN_SPI_MISO) // P1.8 +#define LORA_MOSI (PIN_SPI_MOSI) // P1.9 + +//////////////////////////////////////////////////////////////////////////////// +// GPS (Airoha, dual-band L1+L5) + +#define HAS_GPS 1 +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX +#define GPS_BAUD_RATE (115200) + +#define GPS_EN (43) // P1.11, active HIGH +#define GPS_RESET (8) // P0.8, active HIGH + +#define GPS_VRTC_EN (45) // P1.13 +#define GPS_SLEEP_INT (30) // P0.30 +#define GPS_RTC_INT (29) // P0.29 + +//////////////////////////////////////////////////////////////////////////////// +// Haptic driver (DRV2605 on I2C) + +#define PIN_DRV_EN (37) // P1.5, power to haptic driver + +//////////////////////////////////////////////////////////////////////////////// +// Buzzer + +#define BUZZER_PIN (25) // P0.25, pwm output From e4fbd386afa359d62ef3aea06d0c7a38476369f1 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Thu, 6 Aug 2026 12:37:21 +0200 Subject: [PATCH 2/5] ThinkNode M6: Remove QSPIFLASH=1 build flag to fix companion roles M6 doesn't have flash chip available, so companion roles fail to start. --- variants/thinknode_m6/platformio.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/variants/thinknode_m6/platformio.ini b/variants/thinknode_m6/platformio.ini index 3606d6a8..ad7e6902 100644 --- a/variants/thinknode_m6/platformio.ini +++ b/variants/thinknode_m6/platformio.ini @@ -85,7 +85,6 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D AUTO_SHUTDOWN_MILLIVOLTS=3300 - -D QSPIFLASH=1 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ThinkNode_M6.build_src_filter} @@ -107,7 +106,6 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 - -D QSPIFLASH=1 -D OFFLINE_QUEUE_SIZE=256 -D AUTO_SHUTDOWN_MILLIVOLTS=3300 -D ENABLE_USB_INTERFACE From dee133d647b8f30745761aa45a347c1b47944322 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Fri, 7 Aug 2026 00:19:18 +1200 Subject: [PATCH 3/5] disable cad on companion until it's configurable --- examples/companion_radio/MyMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index b8661caf..b03334c1 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -262,7 +262,7 @@ int MyMesh::getInterferenceThreshold() const { return 0; // disabled for now, until currentRSSI() problem is resolved } bool MyMesh::getCADEnabled() const { - return true; // hardware CAD before TX (no CLI toggle on companion; enabled by default) + return false; // hardware CAD before TX (disabled by default, until configurable) } int MyMesh::calcRxDelay(float score, uint32_t air_time) const { From 3abe415cf7c2623b7b4eb6951d525d89111361f3 Mon Sep 17 00:00:00 2001 From: Hacuchino-hash <246103064+Hacuchino-hash@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:03:44 -0500 Subject: [PATCH 4/5] Add RGB LED and haptic notifications for SenseCAP MeshTracker X1 --- examples/companion_radio/NodePrefs.h | 2 + examples/companion_radio/ui-orig/UITask.cpp | 124 +++++++++++++++++++- examples/companion_radio/ui-orig/UITask.h | 7 ++ src/helpers/ui/DRV2605Vibration.cpp | 47 ++++++++ src/helpers/ui/DRV2605Vibration.h | 39 ++++++ variants/meshtracker_x1/platformio.ini | 11 ++ 6 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 src/helpers/ui/DRV2605Vibration.cpp create mode 100644 src/helpers/ui/DRV2605Vibration.h diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 39a5386a..4ec1803e 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -28,6 +28,7 @@ public: uint32_t ble_pin = 0; uint8_t advert_loc_policy = 0; uint8_t buzzer_quiet = 0; + uint8_t vibe_quiet = 0; uint8_t gps_enabled = 0; // GPS enabled flag (0=disabled, 1=enabled) uint32_t gps_interval = 0; // GPS read interval in seconds uint8_t autoadd_config = 0; // bitmask for auto-add contacts config @@ -101,6 +102,7 @@ private: def("defs_key", (void *) _parent->default_scope_key, sizeof(_parent->default_scope_key)); def("pin", _parent->ble_pin); def("buzz_q", _parent->buzzer_quiet); + def("vibe_q", _parent->vibe_quiet); def("auto_add", _parent->autoadd_config); // bitmask for auto-add contacts config def("man_add", _parent->manual_add_contacts); def("tel_base", _parent->telemetry_mode_base); diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 09fc8e77..08fc9b7a 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -6,12 +6,19 @@ #define AUTO_OFF_MILLIS 15000 // 15 seconds #define BOOT_SCREEN_MILLIS 3000 // 3 seconds -#ifdef PIN_STATUS_LED +#if defined(PIN_STATUS_LED) || defined(PIN_STATUS_LED_R) #define LED_ON_MILLIS 20 #define LED_ON_MSG_MILLIS 200 #define LED_CYCLE_MILLIS 4000 #endif +#if defined(PIN_STATUS_LED_R) && defined(PIN_STATUS_LED_G) && defined(PIN_STATUS_LED_B) +#define STATUS_LED_RGB 1 +#ifndef LOW_BATT_MILLIVOLTS +#define LOW_BATT_MILLIVOLTS 3500 +#endif +#endif + #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW #endif @@ -60,6 +67,11 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no buzzer.startup(); #endif +#ifdef HAS_DRV2605 + vibration.begin(); + vibration.quiet(_node_prefs->vibe_quiet); +#endif + // Initialize digital button if available #ifdef PIN_USER_BTN _userButton = new Button(PIN_USER_BTN, USER_BTN_PRESSED); @@ -130,6 +142,10 @@ void UITask::clearMsgPreview() { void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { _msgcount = msgcount; +#ifdef HAS_DRV2605 + vibration.trigger(); // vibrate even while the app is connected (honors quiet + cooldown) +#endif + if (path_len == 0xFF) { sprintf(_origin, "(F) %s", from_name); } else { @@ -260,8 +276,88 @@ void UITask::renderCurrScreen() { _need_refresh = false; } +#ifdef STATUS_LED_RGB +static void statusLedWrite(uint8_t r, uint8_t g, uint8_t b) { + analogWrite(PIN_STATUS_LED_R, r); + analogWrite(PIN_STATUS_LED_G, g); + analogWrite(PIN_STATUS_LED_B, b); +} + +static void statusLedWheel(uint8_t pos) { // smooth hue sweep, 0..255 + if (pos < 85) { + statusLedWrite(255 - pos * 3, pos * 3, 0); + } else if (pos < 170) { + pos -= 85; + statusLedWrite(0, 255 - pos * 3, pos * 3); + } else { + pos -= 170; + statusLedWrite(pos * 3, 0, 255 - pos * 3); + } +} +#endif + void UITask::userLedHandler() { -#ifdef PIN_STATUS_LED +#ifdef STATUS_LED_RGB + static bool booted = false; + static bool flourish_active = false; + static unsigned long flourish_until = 0; + static int prev_msgcount = 0; + static int state = 0; + static unsigned long next_change = 0; + static int last_increment = 0; + static unsigned long next_batt_check = 0; + static bool low_batt = false; + + unsigned long cur_time = millis(); + + if (!booted) { // color sweep on boot + booted = true; + flourish_until = cur_time + 1200; + flourish_active = true; + } + if (_msgcount > prev_msgcount) { // color sweep when a new message arrives + flourish_until = cur_time + 800; + flourish_active = true; + } + prev_msgcount = _msgcount; + + if (flourish_active) { + if (cur_time < flourish_until) { + statusLedWheel((cur_time % 1200) * 255 / 1200); + return; + } + statusLedWrite(0, 0, 0); + flourish_active = false; + state = 0; + next_change = cur_time; + } + + if (cur_time > next_batt_check) { // battery reads are not free, keep them rare + low_batt = _board->getBattMilliVolts() < LOW_BATT_MILLIVOLTS; + next_batt_check = cur_time + 60000; + } + + if (cur_time > next_change) { + if (state == 0) { + state = 1; + last_increment = (_msgcount > 0) ? LED_ON_MSG_MILLIS : LED_ON_MILLIS; + next_change = cur_time + last_increment; + if (low_batt) { + statusLedWrite(255, 0, 0); // red: battery low + } else if (_msgcount > 0) { + statusLedWrite(255, 90, 0); // amber: unread messages + } else if (_connected) { + statusLedWrite(0, 0, 255); // blue: app connected + } else { + statusLedWrite(0, 255, 0); // green: heartbeat + } + } else { + state = 0; + next_change = cur_time + LED_CYCLE_MILLIS - last_increment; + statusLedWrite(0, 0, 0); + } + } +#elif defined(PIN_STATUS_LED) static int state = 0; static int next_change = 0; static int last_increment = 0; @@ -406,8 +502,26 @@ void UITask::handleButtonDoublePress() { void UITask::handleButtonTriplePress() { MESH_DEBUG_PRINTLN("UITask: triple press triggered"); - // Toggle buzzer quiet mode - #ifdef PIN_BUZZER +#if defined(PIN_BUZZER) && defined(HAS_DRV2605) + // cycle alert modes: Buzz+Vibe -> Buzz only -> Vibe only -> Silent + int mode = (_node_prefs->buzzer_quiet ? 2 : 0) | (_node_prefs->vibe_quiet ? 1 : 0); + mode = (mode + 1) & 3; + _node_prefs->buzzer_quiet = (mode & 2) ? 1 : 0; + _node_prefs->vibe_quiet = (mode & 1) ? 1 : 0; + buzzer.quiet(_node_prefs->buzzer_quiet); + vibration.quiet(_node_prefs->vibe_quiet); + // audible/tactile confirmation of the new mode (no screen on some boards) + if (!_node_prefs->buzzer_quiet) notify(UIEventType::ack); + if (!_node_prefs->vibe_quiet) vibration.trigger(true); + switch (mode) { + case 0: sprintf(_alert, "Alerts: Buzz+Vibe"); break; + case 1: sprintf(_alert, "Alerts: Buzz only"); break; + case 2: sprintf(_alert, "Alerts: Vibe only"); break; + default: sprintf(_alert, "Alerts: Silent"); break; + } + the_mesh.savePrefs(); + _need_refresh = true; +#elif defined(PIN_BUZZER) if (buzzer.isQuiet()) { buzzer.quiet(false); notify(UIEventType::ack); @@ -419,7 +533,7 @@ void UITask::handleButtonTriplePress() { _node_prefs->buzzer_quiet = buzzer.isQuiet(); the_mesh.savePrefs(); _need_refresh = true; - #endif +#endif } void UITask::handleButtonQuadruplePress() { diff --git a/examples/companion_radio/ui-orig/UITask.h b/examples/companion_radio/ui-orig/UITask.h index 961c07a0..02d126e8 100644 --- a/examples/companion_radio/ui-orig/UITask.h +++ b/examples/companion_radio/ui-orig/UITask.h @@ -14,11 +14,18 @@ #include "Button.h" +#ifdef HAS_DRV2605 + #include +#endif + class UITask : public AbstractUITask { DisplayDriver* _display; SensorManager* _sensors; #ifdef PIN_BUZZER genericBuzzer buzzer; +#endif +#ifdef HAS_DRV2605 + DRV2605Vibration vibration; #endif unsigned long _next_refresh, _auto_off; NodePrefs* _node_prefs; diff --git a/src/helpers/ui/DRV2605Vibration.cpp b/src/helpers/ui/DRV2605Vibration.cpp new file mode 100644 index 00000000..3a42bb88 --- /dev/null +++ b/src/helpers/ui/DRV2605Vibration.cpp @@ -0,0 +1,47 @@ +#ifdef HAS_DRV2605 + +#include "DRV2605Vibration.h" +#include + +void DRV2605Vibration::begin() { +#ifdef PIN_DRV_EN + pinMode(PIN_DRV_EN, OUTPUT); + digitalWrite(PIN_DRV_EN, HIGH); // power up the haptic driver + delay(10); +#endif + if (!drv.begin(&Wire)) { + return; // no haptic driver found, stay silent + } +#ifdef DRV2605_USE_LRA + // LRA mode: 4x brake factor, medium loop gain, back-EMF gain 2 + drv.writeRegister8(DRV2605_REG_FEEDBACK, 0xB6); +#endif + drv.selectLibrary(1); + drv.setMode(DRV2605_MODE_INTTRIG); + _ready = true; +} + +void DRV2605Vibration::trigger(bool force) { + if (!_ready || _quiet) return; + unsigned long now = millis(); + if (!force && _last_trigger != 0 && now - _last_trigger < VIBRATION_TIMEOUT) return; + _last_trigger = now; + drv.setWaveform(0, DRV2605_EFFECT); + drv.setWaveform(1, 0); // pause + drv.setWaveform(2, DRV2605_EFFECT); + drv.setWaveform(3, 0); // end of sequence + drv.go(); +} + +void DRV2605Vibration::loop() { +} + +bool DRV2605Vibration::isVibrating() { + return false; // effects are short, treat as instantaneous +} + +void DRV2605Vibration::stop() { + if (_ready) drv.stop(); +} + +#endif // ifdef HAS_DRV2605 diff --git a/src/helpers/ui/DRV2605Vibration.h b/src/helpers/ui/DRV2605Vibration.h new file mode 100644 index 00000000..a40894be --- /dev/null +++ b/src/helpers/ui/DRV2605Vibration.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef HAS_DRV2605 + +#include +#include + +/* + * Vibration control class for boards where the motor is behind a + * DRV2605 haptic driver on I2C (e.g. Seeed SenseCAP MeshTracker X1). + * Same interface as GenericVibration. + */ + +#ifndef VIBRATION_TIMEOUT +#define VIBRATION_TIMEOUT 5000 // cooldown between vibrations +#endif + +#ifndef DRV2605_EFFECT +#define DRV2605_EFFECT 16 // "1000 ms alert" from the DRV2605 effect library +#endif + +class DRV2605Vibration { +public: + void begin(); // power up and init the DRV2605 + void trigger(bool force = false); // trigger vibration; force skips the cooldown + void loop(); // no-op, the DRV2605 plays effects autonomously + bool isVibrating(); + void stop(); // stop vibration immediately + void quiet(bool q) { _quiet = q; } + bool isQuiet() const { return _quiet; } + +private: + Adafruit_DRV2605 drv; + bool _ready = false; + bool _quiet = false; + unsigned long _last_trigger = 0; +}; + +#endif // ifdef HAS_DRV2605 diff --git a/variants/meshtracker_x1/platformio.ini b/variants/meshtracker_x1/platformio.ini index dbab5ca6..18506916 100644 --- a/variants/meshtracker_x1/platformio.ini +++ b/variants/meshtracker_x1/platformio.ini @@ -12,6 +12,9 @@ build_flags = ${nrf52_base.build_flags} -D PIN_USER_BTN=6 -D USER_BTN_PRESSED=HIGH -D PIN_STATUS_LED=24 + -D PIN_STATUS_LED_R=3 ; P0.3 red + -D PIN_STATUS_LED_G=24 ; P0.24 green + -D PIN_STATUS_LED_B=28 ; P0.28 blue -D RADIO_CLASS=CustomLR2021 -D USE_LR2021 -D WRAPPER_CLASS=CustomLR2021Wrapper @@ -77,16 +80,20 @@ build_flags = ${MeshTracker_X1.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=NullDisplayDriver -D PIN_BUZZER=25 + -D HAS_DRV2605=1 + -D DRV2605_USE_LRA=1 -D ENABLE_USB_INTERFACE build_src_filter = ${MeshTracker_X1.build_src_filter} + + + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${MeshTracker_X1.lib_deps} densaugeo/base64 @ ~1.4.0 stevemarple/MicroNMEA @ ^2.0.6 end2endzone/NonBlockingRTTTL@^1.3.0 + adafruit/Adafruit DRV2605 Library @ ^1.2.4 [env:MeshTracker_X1_companion_radio_ble] extends = MeshTracker_X1 @@ -104,14 +111,18 @@ build_flags = ${MeshTracker_X1.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=NullDisplayDriver -D PIN_BUZZER=25 + -D HAS_DRV2605=1 + -D DRV2605_USE_LRA=1 -D ADVERT_NAME='"@@MAC"' build_src_filter = ${MeshTracker_X1.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> +<../examples/companion_radio/ui-orig/*.cpp> lib_deps = ${MeshTracker_X1.lib_deps} densaugeo/base64 @ ~1.4.0 stevemarple/MicroNMEA @ ^2.0.6 end2endzone/NonBlockingRTTTL@^1.3.0 + adafruit/Adafruit DRV2605 Library @ ^1.2.4 From 335ebf546d6f1f15c0bf4dbce08dd8a65d1ceb2c Mon Sep 17 00:00:00 2001 From: Hacuchino-hash <246103064+Hacuchino-hash@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:34:02 -0500 Subject: [PATCH 5/5] Add SPA06 barometer telemetry and charge status LED for MeshTracker X1 --- examples/companion_radio/ui-orig/UITask.cpp | 30 +++++++++++++++++++++ variants/meshtracker_x1/platformio.ini | 2 ++ variants/meshtracker_x1/target.cpp | 16 +++++++++++ variants/meshtracker_x1/target.h | 3 +++ variants/meshtracker_x1/variant.cpp | 3 ++- variants/meshtracker_x1/variant.h | 1 + 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 08fc9b7a..1e961316 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -337,6 +337,36 @@ void UITask::userLedHandler() { next_batt_check = cur_time + 60000; } +#ifdef EXT_CHRG_DETECT + // charge status display while docked (skipped when messages are waiting, + // so the unread indication is never masked) + static unsigned long next_pwr_check = 0; + static bool ext_powered = false, ext_charging = false; + if (cur_time > next_pwr_check) { + ext_powered = _board->isExternalPowered(); + bool chrg = digitalRead(EXT_CHRG_DETECT) == LOW; + #ifdef EXT_CHRG_DONE + if (digitalRead(EXT_CHRG_DONE) == LOW) chrg = false; // charge-done wins + #endif + ext_charging = ext_powered && chrg; + next_pwr_check = cur_time + 1000; + } + if (ext_powered && _msgcount == 0) { + if (ext_charging) { + // amber breathing while charging + int ph = cur_time % 2000; + int v = ph < 1000 ? ph : 2000 - ph; + uint8_t lvl = (uint8_t)(v * 255 / 1000); + statusLedWrite(lvl, (uint8_t)(lvl * 35 / 100), 0); + } else { + statusLedWrite(0, 40, 0); // dim solid green: charge complete + } + state = 0; + next_change = cur_time; + return; + } +#endif + if (cur_time > next_change) { if (state == 0) { state = 1; diff --git a/variants/meshtracker_x1/platformio.ini b/variants/meshtracker_x1/platformio.ini index 18506916..6a46a79f 100644 --- a/variants/meshtracker_x1/platformio.ini +++ b/variants/meshtracker_x1/platformio.ini @@ -32,6 +32,8 @@ build_flags = ${nrf52_base.build_flags} build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/meshtracker_x1> +lib_deps = ${nrf52_base.lib_deps} + https://github.com/adafruit/Adafruit_SPA06_003/archive/refs/tags/1.0.2.zip debug_tool = jlink upload_protocol = nrfutil diff --git a/variants/meshtracker_x1/target.cpp b/variants/meshtracker_x1/target.cpp index 1139b8b7..d6d548b5 100644 --- a/variants/meshtracker_x1/target.cpp +++ b/variants/meshtracker_x1/target.cpp @@ -67,6 +67,17 @@ void MeshTrackerX1SensorManager::stop_gps() { bool MeshTrackerX1SensorManager::begin() { // init GPS Serial1.begin(GPS_BAUD_RATE); + + // init SPA06-003 barometer + baro_ok = spa06.begin(SPA06_003_DEFAULT_ADDR, &Wire) || spa06.begin(0x76, &Wire); + if (baro_ok) { + spa06.setPressureOversampling(SPA06_003_OVERSAMPLE_8); + spa06.setTemperatureOversampling(SPA06_003_OVERSAMPLE_8); + // 1 Hz continuous keeps reads non-blocking at minimal power cost + spa06.setPressureMeasureRate(SPA06_003_RATE_1); + spa06.setTemperatureMeasureRate(SPA06_003_RATE_1); + spa06.setMeasurementMode(SPA06_003_MEAS_CONTINUOUS_BOTH); + } return true; } @@ -74,12 +85,17 @@ bool MeshTrackerX1SensorManager::querySensors(uint8_t requester_permissions, Cay if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } + if (requester_permissions & TELEM_PERM_ENVIRONMENT && baro_ok) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, spa06.readTemperature()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, spa06.readPressure()); + } return true; } void MeshTrackerX1SensorManager::loop() { static long next_gps_update = 0; + _nmea->loop(); if (millis() > next_gps_update) { diff --git a/variants/meshtracker_x1/target.h b/variants/meshtracker_x1/target.h index 665f4328..9741efb6 100644 --- a/variants/meshtracker_x1/target.h +++ b/variants/meshtracker_x1/target.h @@ -8,12 +8,15 @@ #include #include #include +#include #ifdef DISPLAY_CLASS #include "NullDisplayDriver.h" #endif class MeshTrackerX1SensorManager: public SensorManager { bool gps_active = false; + bool baro_ok = false; + Adafruit_SPA06_003 spa06; LocationProvider * _nmea; void start_gps(); diff --git a/variants/meshtracker_x1/variant.cpp b/variants/meshtracker_x1/variant.cpp index a0e64380..5a685a8e 100644 --- a/variants/meshtracker_x1/variant.cpp +++ b/variants/meshtracker_x1/variant.cpp @@ -62,7 +62,8 @@ const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] = void initVariant() { pinMode(BATTERY_PIN, INPUT); - pinMode(EXT_CHRG_DETECT, INPUT); + pinMode(EXT_CHRG_DETECT, INPUT_PULLUP); + pinMode(EXT_CHRG_DONE, INPUT_PULLUP); pinMode(EXT_PWR_DETECT, INPUT); pinMode(PIN_BUTTON1, INPUT_PULLDOWN); diff --git a/variants/meshtracker_x1/variant.h b/variants/meshtracker_x1/variant.h index 8e9bae8f..c3dbd274 100644 --- a/variants/meshtracker_x1/variant.h +++ b/variants/meshtracker_x1/variant.h @@ -25,6 +25,7 @@ #define ADC_MULTIPLIER (2.0F) #define EXT_CHRG_DETECT (35) // P1.3, LOW while charging +#define EXT_CHRG_DONE (36) // P1.4, LOW when charge complete #define EXT_PWR_DETECT (5) // P0.5 #define ADC_RESOLUTION (14)