mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-08 18:56:40 +00:00
feat(wio-tracker-l2): board layer + ui-new bring-up env
Seeed Wio Tracker L2 (ESP32-S3R8 16MB/8MB PSRAM, SX1262, NV3031B 320x240 QSPI LCD + GT911 touch + LP5814 backlight, TCA9535 expander gating every rail, ADS1115 battery ADC, AW35615 USB-C, L76K GNSS). Board layer ported from meshcore-dev/MeshCore PR #3381 (Hacuchino-hash), kept at the same paths for an easy upstream merge. Differences: - display: dropped the UIColor theme (not adopted in this fork) - GpsTap diagnostics wrapper omitted; GNSS reads Serial1 directly - nv3031b ODR fix dropped: LovyanGFX >= 1.2.30 (now pinned) defines Panel_NV3031B::init_cmds itself -- the fix became a duplicate symbol - MESH_DEBUG_PRINTLN with a non-literal format fixed (broke MESH_DEBUG builds) - upload_speed 115200: baud is virtual on native USB; skips the switch solo/wio-tracker-l2 adds Wio_Tracker_L2_companion_solo_bringup: ui-new zoomed 2x, BOOT button only, no touch -- a hardware smoke test, not a release target (deliberately not *_solo_dual, so CI doesn't publish it). The real UI for this board will be a new LVGL frontend. Verified on hardware: boot, display, radio, GPS, battery, BLE app link. First boot takes tens of seconds while SPIFFS formats its 3.4 MB partition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi",
|
||||
"partitions": "default_16MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Seeed Wio Tracker L2 (16 MB flash, 8 MB OPI 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": 921600
|
||||
},
|
||||
"url": "https://www.seeedstudio.com/",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
; Solo build for Seeed Wio Tracker L2 -- extends the board base defined in
|
||||
; variants/wio-tracker-l2/platformio.ini. Solo configs live in their own
|
||||
; top-level solo/ folder, separate from the shared upstream board variants.
|
||||
;
|
||||
; BRING-UP ONLY: this env runs the existing ui-new (OLED-era, joystick-driven)
|
||||
; UI zoomed 2x (160x120 logical on the 320x240 panel) purely to compile-test
|
||||
; and smoke-test the board layer -- display, radio, GPS, battery. It has no
|
||||
; touch input yet; the only control is the BOOT button. The real interface for
|
||||
; this board is the planned LVGL frontend over a shared UI Core.
|
||||
;
|
||||
; Deliberately NOT named *_solo_dual yet, so build-solo-firmwares.yml doesn't
|
||||
; publish it as a release artifact.
|
||||
[env:Wio_Tracker_L2_companion_solo_bringup]
|
||||
extends = Wio_Tracker_L2
|
||||
build_flags =
|
||||
${Wio_Tracker_L2.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D DISPLAY_CLASS=WioTrackerL2Display
|
||||
-D UI_ZOOM=2
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D DUAL_SERIAL=1
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D FIRMWARE_SOLO_BUILD=1
|
||||
-D MESHCORE_VERSION='"1.17.1"'
|
||||
-D OLED_MISC_FIXED_FONT=1
|
||||
-D AUTO_OFF_MILLIS=60000
|
||||
build_src_filter = ${Wio_Tracker_L2.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<helpers/ui/LGFXDisplay.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${Wio_Tracker_L2.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
@@ -0,0 +1,204 @@
|
||||
#include "WioTrackerL2Board.h"
|
||||
|
||||
// TCA9535 register map
|
||||
static const uint8_t TCA_REG_OUTPUT0 = 0x02;
|
||||
static const uint8_t TCA_REG_OUTPUT1 = 0x03;
|
||||
static const uint8_t TCA_REG_CONFIG0 = 0x06; // 1 = input, 0 = output
|
||||
static const uint8_t TCA_REG_CONFIG1 = 0x07;
|
||||
|
||||
// ADS1115 registers / config bits
|
||||
static const uint8_t ADS_REG_CONVERSION = 0x00;
|
||||
static const uint8_t ADS_REG_CONFIG = 0x01;
|
||||
// OS=1 (start single shot) | MUX=100 (AIN0 vs GND) | PGA=001 (+/-4.096V)
|
||||
// | MODE=1 (single shot) | DR=100 (128 SPS) | COMP_QUE=11 (disabled)
|
||||
static const uint16_t ADS_CFG_BATT = 0x8000 | 0x4000 | 0x0200 | 0x0100 | 0x0080 | 0x0003;
|
||||
// +/-4.096V FSR -> 125uV/LSB; battery is behind a x2 divider -> 0.25 mV/LSB
|
||||
static const float ADS_MV_PER_LSB = 0.125f * 2.0f;
|
||||
|
||||
bool WioTrackerL2Board::expWriteReg(uint8_t reg, uint8_t val) {
|
||||
Wire.beginTransmission(TCA9535_ADDR);
|
||||
Wire.write(reg);
|
||||
Wire.write(val);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::expSetOutput(uint8_t pin, bool initial_level) {
|
||||
uint8_t port = pin >> 3, bit = pin & 7;
|
||||
// set desired level first so the pin doesn't glitch when direction flips
|
||||
if (initial_level) out_shadow[port] |= (1 << bit);
|
||||
else out_shadow[port] &= ~(1 << bit);
|
||||
expWriteReg(port ? TCA_REG_OUTPUT1 : TCA_REG_OUTPUT0, out_shadow[port]);
|
||||
|
||||
cfg_shadow[port] &= ~(1 << bit); // 0 = output
|
||||
expWriteReg(port ? TCA_REG_CONFIG1 : TCA_REG_CONFIG0, cfg_shadow[port]);
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::expSetInput(uint8_t pin) {
|
||||
uint8_t port = pin >> 3, bit = pin & 7;
|
||||
cfg_shadow[port] |= (1 << bit);
|
||||
expWriteReg(port ? TCA_REG_CONFIG1 : TCA_REG_CONFIG0, cfg_shadow[port]);
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::expWritePin(uint8_t pin, bool level) {
|
||||
uint8_t port = pin >> 3, bit = pin & 7;
|
||||
if (level) out_shadow[port] |= (1 << bit);
|
||||
else out_shadow[port] &= ~(1 << bit);
|
||||
expWriteReg(port ? TCA_REG_OUTPUT1 : TCA_REG_OUTPUT0, out_shadow[port]);
|
||||
}
|
||||
|
||||
// Power-up sequence mirrors Seeed's reference firmware for this board
|
||||
// (order and delays matter, especially the LCD reset settle time).
|
||||
bool WioTrackerL2Board::initExpander() {
|
||||
Wire.beginTransmission(TCA9535_ADDR);
|
||||
if (Wire.endTransmission() != 0) {
|
||||
return false; // expander not responding
|
||||
}
|
||||
|
||||
expSetInput(EXP_PIN_WAKE_BTN);
|
||||
expSetInput(EXP_PIN_I2C_IRQ);
|
||||
expSetInput(EXP_PIN_SD_DETECT);
|
||||
|
||||
expSetOutput(EXP_PIN_OTG_EN, LOW);
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_PA_EN, LOW); // amp only powered while a tone plays
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_TF_EN, HIGH);
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_BAT_ADC_EN, HIGH);
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_GNSS_EN, HIGH);
|
||||
delay(10);
|
||||
// GNSS reset is active HIGH; hold 10ms then release LOW so the L76K runs
|
||||
expSetOutput(EXP_PIN_GNSS_RST, HIGH);
|
||||
delay(10);
|
||||
expWritePin(EXP_PIN_GNSS_RST, LOW);
|
||||
// user LED is driven active-low through the expander; idle level HIGH = off
|
||||
expSetOutput(EXP_PIN_USER_LED, HIGH);
|
||||
delay(10);
|
||||
expSetOutput(EXP_PIN_GROVE_EN, HIGH);
|
||||
delay(10);
|
||||
|
||||
expSetOutput(EXP_PIN_LCD_EN, HIGH);
|
||||
delay(50);
|
||||
expSetOutput(EXP_PIN_LCD_RST, HIGH);
|
||||
delay(5);
|
||||
expWritePin(EXP_PIN_LCD_RST, LOW);
|
||||
delay(10);
|
||||
expWritePin(EXP_PIN_LCD_RST, HIGH);
|
||||
delay(500); // NV3031B needs a long settle after reset before init commands
|
||||
expSetOutput(EXP_PIN_LCD_CS, HIGH);
|
||||
delay(10);
|
||||
|
||||
// GT911 touch reset sequence: INT low during reset selects I2C addr 0x5D
|
||||
expSetOutput(EXP_PIN_TP_RST, LOW);
|
||||
expSetOutput(EXP_PIN_TP_INT, LOW);
|
||||
delay(10);
|
||||
expWritePin(EXP_PIN_TP_RST, HIGH);
|
||||
delay(60);
|
||||
|
||||
// capture WAKE button idle level (polarity unknown on alpha hardware)
|
||||
int inputs = expReadInputs();
|
||||
wake_btn_baseline = inputs >= 0 ? (uint8_t)(inputs & 1) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::begin() {
|
||||
// GNSS UART: NMEA arrives at ~500 B/s and the main loop can stall for
|
||||
// hundreds of ms on SD tile decodes; the 256-byte default overflowed
|
||||
Serial1.setRxBufferSize(1024);
|
||||
|
||||
uint32_t t0 = millis();
|
||||
while (!Serial && millis() - t0 < 3000) { delay(50); }
|
||||
MESH_DEBUG_PRINTLN("WioTrackerL2Board: ESP32Board init");
|
||||
|
||||
ESP32Board::begin(); // starts Wire on PIN_BOARD_SDA/PIN_BOARD_SCL (47/48)
|
||||
|
||||
pinMode(PIN_USER_BTN, INPUT_PULLUP);
|
||||
pinMode(P_LORA_MISO, INPUT_PULLUP);
|
||||
|
||||
MESH_DEBUG_PRINTLN("WioTrackerL2Board: TCA9535 expander init");
|
||||
expander_ok = initExpander();
|
||||
if (!expander_ok) {
|
||||
Serial.println("ERROR: TCA9535 IO expander not found - peripherals unpowered!");
|
||||
}
|
||||
|
||||
Wire.beginTransmission(0x22);
|
||||
aw_ok = Wire.endTransmission() == 0;
|
||||
MESH_DEBUG_PRINTLN("WioTrackerL2Board: init done");
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) {
|
||||
startup_reason = BD_STARTUP_RX_PACKET; // LoRa packet woke us from deep sleep
|
||||
}
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
}
|
||||
}
|
||||
|
||||
void WioTrackerL2Board::setLed(bool on) {
|
||||
// P10 doubles as GNSS wakeup and idles HIGH; blink = brief LOW pulses so
|
||||
// the GPS never sees a sustained low level
|
||||
if (expander_ok) {
|
||||
expWritePin(EXP_PIN_USER_LED, !on);
|
||||
}
|
||||
}
|
||||
|
||||
int WioTrackerL2Board::expReadInputs() {
|
||||
Wire.beginTransmission(TCA9535_ADDR);
|
||||
Wire.write((uint8_t)0x00); // input port 0 register
|
||||
if (Wire.endTransmission() != 0) return -1;
|
||||
if (Wire.requestFrom((int)TCA9535_ADDR, 2) != 2) return -1;
|
||||
int lo = Wire.read();
|
||||
int hi = Wire.read();
|
||||
return lo | (hi << 8);
|
||||
}
|
||||
|
||||
bool WioTrackerL2Board::isExternalPowered() {
|
||||
if (!aw_ok) return false;
|
||||
Wire.beginTransmission(0x22);
|
||||
Wire.write((uint8_t)0x40); // STATUS0
|
||||
if (Wire.endTransmission() != 0) return false;
|
||||
if (Wire.requestFrom(0x22, 1) != 1) return false;
|
||||
return (Wire.read() & 0x80) != 0; // VBUSOK bit
|
||||
}
|
||||
|
||||
bool WioTrackerL2Board::readWakeButton() {
|
||||
if (!expander_ok) return false;
|
||||
int v = expReadInputs();
|
||||
if (v < 0) return false;
|
||||
return ((uint8_t)(v & 1)) != wake_btn_baseline;
|
||||
}
|
||||
|
||||
int16_t WioTrackerL2Board::adsReadRaw() {
|
||||
Wire.beginTransmission(ADS1115_ADDR);
|
||||
Wire.write(ADS_REG_CONFIG);
|
||||
Wire.write((uint8_t)(ADS_CFG_BATT >> 8));
|
||||
Wire.write((uint8_t)(ADS_CFG_BATT & 0xFF));
|
||||
if (Wire.endTransmission() != 0) return -1;
|
||||
|
||||
delay(10); // 128 SPS -> ~8ms conversion time
|
||||
|
||||
Wire.beginTransmission(ADS1115_ADDR);
|
||||
Wire.write(ADS_REG_CONVERSION);
|
||||
if (Wire.endTransmission() != 0) return -1;
|
||||
if (Wire.requestFrom((int)ADS1115_ADDR, 2) != 2) return -1;
|
||||
|
||||
// sequence the two reads explicitly: operand evaluation order of <<|
|
||||
// is unspecified and each read dequeues a byte
|
||||
uint8_t hi = Wire.read();
|
||||
uint8_t lo = Wire.read();
|
||||
int16_t raw = ((int16_t)hi << 8) | lo;
|
||||
return raw < 0 ? 0 : raw;
|
||||
}
|
||||
|
||||
uint16_t WioTrackerL2Board::getBattMilliVolts() {
|
||||
if (!expander_ok) return 0; // BAT_ADC_EN rail never came up
|
||||
|
||||
int16_t raw = adsReadRaw();
|
||||
if (raw < 0) return 0;
|
||||
|
||||
return (uint16_t)(raw * ADS_MV_PER_LSB);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "helpers/ESP32Board.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Seeed Wio Tracker L2 Pro (Wio-S3 module: ESP32-S3 + SX1262)
|
||||
//
|
||||
// Nearly all peripheral power rails and reset lines are behind a TCA9535
|
||||
// 16-bit I2C IO expander at address 0x21. Nothing (GNSS, LCD, touch, SD,
|
||||
// battery ADC, speaker amp) works until the expander is configured, so
|
||||
// begin() must run before any display / GPS / sensor init.
|
||||
//
|
||||
// Battery voltage is read through an ADS1115 16-bit I2C ADC at 0x48,
|
||||
// channel AIN0, behind a x2 resistive divider (rail gated by expander).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define TCA9535_ADDR 0x21
|
||||
#define ADS1115_ADDR 0x48
|
||||
|
||||
// TCA9535 pin numbering: 0..7 = port 0 (P00..P07), 8..15 = port 1 (P10..P17)
|
||||
#define EXP_PIN_WAKE_BTN 0 // input - side WAKE button
|
||||
#define EXP_PIN_I2C_IRQ 1 // input - shared I2C IRQ
|
||||
#define EXP_PIN_SD_DETECT 2 // input - microSD card detect
|
||||
#define EXP_PIN_TP_INT 3 // output - touch panel interrupt (driven for reset seq)
|
||||
#define EXP_PIN_LCD_CS 4 // output - LCD chip select (idle high)
|
||||
#define EXP_PIN_LCD_EN 5 // output - LCD power enable
|
||||
#define EXP_PIN_LCD_RST 6 // output - LCD reset
|
||||
#define EXP_PIN_GROVE_EN 7 // output - Grove port power
|
||||
#define EXP_PIN_TP_RST 8 // output - touch panel reset
|
||||
#define EXP_PIN_GNSS_RST 9 // output - GNSS reset (active HIGH)
|
||||
#define EXP_PIN_USER_LED 10 // output - mesh/user LED (also GNSS wakeup)
|
||||
#define EXP_PIN_OTG_EN 11 // output - USB OTG power
|
||||
#define EXP_PIN_PA_EN 12 // output - speaker amp power
|
||||
#define EXP_PIN_GNSS_EN 13 // output - GNSS power
|
||||
#define EXP_PIN_TF_EN 14 // output - microSD power
|
||||
#define EXP_PIN_BAT_ADC_EN 15 // output - battery ADC divider enable
|
||||
|
||||
class WioTrackerL2Board : public ESP32Board {
|
||||
public:
|
||||
void begin();
|
||||
|
||||
uint16_t getBattMilliVolts() override;
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
return "Seeed Wio Tracker L2 Pro";
|
||||
}
|
||||
|
||||
void onBeforeTransmit() override { setLed(true); }
|
||||
void onAfterTransmit() override { setLed(false); }
|
||||
|
||||
// Mesh/user LED lives on the IO expander, not a GPIO
|
||||
void setLed(bool on);
|
||||
|
||||
// speaker amplifier power (expander P12); off when idle to keep the GNSS
|
||||
// antenna away from class-D switching noise and to save battery
|
||||
void setSpeakerAmp(bool on) { expWritePin(EXP_PIN_PA_EN, on); }
|
||||
|
||||
// hardware reset pulse to the L76K GNSS (active HIGH on this board); the
|
||||
// module keeps almanac/ephemeris across it, so this is a warm restart
|
||||
// GNSS rail: off saves the whole receiver when GPS is disabled; on re-runs
|
||||
// the power-up reset so the module comes back cleanly
|
||||
void setGnssPower(bool on) {
|
||||
if (on) {
|
||||
expWritePin(EXP_PIN_GNSS_EN, HIGH);
|
||||
delay(10);
|
||||
gnssReset();
|
||||
} else {
|
||||
expWritePin(EXP_PIN_GNSS_EN, LOW);
|
||||
}
|
||||
}
|
||||
// Grove expansion port rail (nothing on-board depends on it)
|
||||
void setGrovePower(bool on) { expWritePin(EXP_PIN_GROVE_EN, on); }
|
||||
|
||||
void gnssReset() {
|
||||
expWritePin(EXP_PIN_GNSS_RST, HIGH);
|
||||
delay(10);
|
||||
expWritePin(EXP_PIN_GNSS_RST, LOW);
|
||||
}
|
||||
|
||||
// WAKE button on expander P00: pressed = level differs from boot baseline
|
||||
bool readWakeButton();
|
||||
|
||||
// VBUS presence via the AW35615 USB-C controller (I2C 0x22)
|
||||
bool isExternalPowered() override;
|
||||
|
||||
bool expanderOK() const { return expander_ok; }
|
||||
|
||||
private:
|
||||
uint8_t out_shadow[2] = { 0xFF, 0xFF }; // TCA9535 output regs default high
|
||||
uint8_t cfg_shadow[2] = { 0xFF, 0xFF }; // 1 = input (power-on default)
|
||||
bool expander_ok = false;
|
||||
bool aw_ok = false; // AW35615 USB-C controller responded at probe
|
||||
uint8_t wake_btn_baseline = 0; // idle level of P00, captured at init
|
||||
|
||||
int expReadInputs(); // 16-bit input register pair, -1 on error
|
||||
|
||||
bool expWriteReg(uint8_t reg, uint8_t val);
|
||||
void expSetOutput(uint8_t pin, bool initial_level);
|
||||
void expSetInput(uint8_t pin);
|
||||
void expWritePin(uint8_t pin, bool level);
|
||||
bool initExpander();
|
||||
|
||||
int16_t adsReadRaw();
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
|
||||
#include <helpers/ui/LGFXDisplay.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#define LGFX_USE_V1
|
||||
#include <LovyanGFX.hpp>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wio Tracker L2 Pro display stack:
|
||||
// - NV3031B 320x240 panel on quad-SPI (SCLK 42, IO0-3 = 41/40/39/38, CS 46)
|
||||
// - GT911 capacitive touch on I2C 0x5D (SDA 47 / SCL 48)
|
||||
// - LP5814 4-channel LED driver on I2C 0x2C used as backlight
|
||||
// Panel power/reset lines are on the TCA9535 expander and are already
|
||||
// sequenced by WioTrackerL2Board::begin() before this driver initializes.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifndef L2_SPI_FREQUENCY
|
||||
#define L2_SPI_FREQUENCY 75000000
|
||||
#endif
|
||||
|
||||
#define LP5814_I2C_ADDR 0x2C
|
||||
|
||||
// LP5814 used as backlight controller (all 4 channels in parallel)
|
||||
class WioTrackerL2Backlight : public lgfx::v1::ILight {
|
||||
static constexpr uint8_t REG_DEVICE_CONFIG0 = 0x00;
|
||||
static constexpr uint8_t REG_MAX_CURRENT = 0x01;
|
||||
static constexpr uint8_t REG_ENABLE_CONTROL = 0x02;
|
||||
static constexpr uint8_t REG_DIM_MODE = 0x04;
|
||||
static constexpr uint8_t REG_ENGINE_MODE = 0x05;
|
||||
static constexpr uint8_t REG_UPDATE = 0x0F;
|
||||
static constexpr uint8_t REG_LED0_DC = 0x14;
|
||||
static constexpr uint8_t REG_LED0_PWM = 0x18;
|
||||
|
||||
uint8_t _brightness = 153; // 60%
|
||||
|
||||
void writeReg(uint8_t reg, uint8_t value) {
|
||||
Wire.beginTransmission(LP5814_I2C_ADDR);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
public:
|
||||
bool init(uint8_t brightness) override {
|
||||
Wire.beginTransmission(LP5814_I2C_ADDR);
|
||||
if (Wire.endTransmission() != 0) {
|
||||
return false; // LP5814 not found
|
||||
}
|
||||
|
||||
writeReg(REG_DEVICE_CONFIG0, 0x01); // chip enable
|
||||
writeReg(REG_MAX_CURRENT, 0x01); // 51 mA max current
|
||||
writeReg(REG_ENABLE_CONTROL, 0x00); // outputs off while configuring
|
||||
writeReg(REG_DIM_MODE, 0x4E);
|
||||
writeReg(REG_ENGINE_MODE, 0xF0);
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
writeReg(REG_LED0_DC + i, 200);
|
||||
}
|
||||
writeReg(REG_ENABLE_CONTROL, 0x0F); // enable all 4 channels
|
||||
writeReg(REG_UPDATE, 0x55); // latch (LP5814 requires 0x55)
|
||||
delay(5);
|
||||
|
||||
setBrightness(brightness);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setBrightness(uint8_t brightness) override {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
writeReg(REG_LED0_PWM + i, brightness);
|
||||
}
|
||||
_brightness = brightness;
|
||||
}
|
||||
|
||||
uint8_t getBrightness() const { return _brightness; }
|
||||
|
||||
virtual ~WioTrackerL2Backlight() = default;
|
||||
};
|
||||
|
||||
class LGFX_WioTrackerL2 : public lgfx::LGFX_Device {
|
||||
lgfx::Panel_NV3031B _panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
WioTrackerL2Backlight _light_instance;
|
||||
|
||||
public:
|
||||
bool init_impl(bool use_reset, bool use_clear) override {
|
||||
// bring up backlight controller while the I2C bus is still clean
|
||||
_light_instance.init(_light_instance.getBrightness());
|
||||
|
||||
bool result = LGFX_Device::init_impl(use_reset, use_clear);
|
||||
|
||||
// GT911 probe can leave the ESP32 I2C peripheral with a stuck BUSY flag;
|
||||
// cycling Wire resets it so later LP5814/sensor traffic doesn't time out
|
||||
Wire.end();
|
||||
Wire.begin(47, 48);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LGFX_WioTrackerL2(void) {
|
||||
{
|
||||
auto cfg = _bus_instance.config();
|
||||
cfg.spi_host = SPI3_HOST;
|
||||
cfg.spi_mode = 3;
|
||||
cfg.freq_write = L2_SPI_FREQUENCY;
|
||||
cfg.freq_read = 16000000;
|
||||
cfg.pin_sclk = 42;
|
||||
// quad SPI data pins
|
||||
cfg.pin_io0 = 41;
|
||||
cfg.pin_io1 = 40;
|
||||
cfg.pin_io2 = 39;
|
||||
cfg.pin_io3 = 38;
|
||||
_bus_instance.config(cfg);
|
||||
_panel_instance.setBus(&_bus_instance);
|
||||
}
|
||||
|
||||
{
|
||||
auto cfg = _panel_instance.config();
|
||||
cfg.pin_cs = 46;
|
||||
cfg.pin_rst = -1; // reset is on the IO expander, done in board init
|
||||
cfg.pin_busy = -1;
|
||||
cfg.panel_width = 240; // native portrait orientation
|
||||
cfg.panel_height = 320;
|
||||
cfg.memory_width = 240;
|
||||
cfg.memory_height = 320;
|
||||
cfg.offset_x = 0;
|
||||
cfg.offset_y = 0;
|
||||
cfg.offset_rotation = 1; // panel mounted landscape (320x240)
|
||||
cfg.invert = true;
|
||||
cfg.rgb_order = true;
|
||||
cfg.dlen_16bit = false;
|
||||
cfg.bus_shared = false;
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
{
|
||||
auto cfg = _touch_instance.config();
|
||||
cfg.pin_cs = -1;
|
||||
cfg.x_min = 0;
|
||||
cfg.x_max = 239;
|
||||
cfg.y_min = 0;
|
||||
cfg.y_max = 319;
|
||||
cfg.pin_int = -1; // INT is on the IO expander
|
||||
cfg.offset_rotation = 2;
|
||||
cfg.i2c_port = 0;
|
||||
cfg.i2c_addr = 0x5D;
|
||||
cfg.pin_sda = 47;
|
||||
cfg.pin_scl = 48;
|
||||
cfg.bus_shared = false;
|
||||
cfg.freq = 400000;
|
||||
_touch_instance.config(cfg);
|
||||
_panel_instance.setTouch(&_touch_instance);
|
||||
}
|
||||
|
||||
_panel_instance.setLight(&_light_instance);
|
||||
setPanel(&_panel_instance);
|
||||
}
|
||||
};
|
||||
|
||||
class WioTrackerL2Display : public LGFXDisplay {
|
||||
LGFX_WioTrackerL2 disp;
|
||||
public:
|
||||
WioTrackerL2Display() : LGFXDisplay(320, 240, disp) {}
|
||||
|
||||
// direct access to the LGFX device (LVGL flush/touch glue)
|
||||
lgfx::LGFX_Device* lgfxDevice() { return &disp; }
|
||||
|
||||
// reliable touch read in logical (UI_ZOOM-scaled) coords; returns false when
|
||||
// not touched (LGFXDisplay::getTouch reads an uninitialized point on release)
|
||||
bool readTouch(int& x, int& y) {
|
||||
lgfx::touch_point_t tp;
|
||||
if (disp.getTouch(&tp, 1) == 0) return false;
|
||||
x = tp.x / UI_ZOOM;
|
||||
y = tp.y / UI_ZOOM;
|
||||
return true;
|
||||
}
|
||||
|
||||
// shadows LGFXDisplay::begin() (non-virtual, called on the concrete type in
|
||||
// main.cpp) - identical except rotation: our panel config already carries
|
||||
// offset_rotation=1 for the landscape mounting, so no extra rotation here
|
||||
bool begin() {
|
||||
turnOn();
|
||||
display->init();
|
||||
display->setRotation(0);
|
||||
display->setColorDepth(8);
|
||||
// The zoomed sprite blit stops a pixel short of the panel edge, so those
|
||||
// pixels keep whatever the panel powered up with (they showed as coloured
|
||||
// lines down the right side and along the bottom). Clear the panel once.
|
||||
display->fillScreen(TFT_BLACK);
|
||||
display->setBrightness(153);
|
||||
display->setTextColor(TFT_WHITE);
|
||||
|
||||
buffer.setColorDepth(8);
|
||||
buffer.setPsram(true);
|
||||
buffer.createSprite(width(), height());
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
; Seeed Wio Tracker L2 - Wio-S3 module: ESP32-S3R8 + SX1262
|
||||
; Board layer ported from meshcore-dev/MeshCore PR #3381 (Hacuchino-hash), whose
|
||||
; pin map was derived from Seeed's Meshtastic support branch (meshtastic/firmware
|
||||
; PR #10909 + device-ui `wio-l2` branch). Pre-release hardware: verify the pin
|
||||
; map against a real unit.
|
||||
[Wio_Tracker_L2]
|
||||
extends = esp32_base
|
||||
board = seeed_wio_tracker_l2
|
||||
; Native USB-Serial/JTAG: the baud rate is virtual (no effect on transfer
|
||||
; speed); staying at the ROM's 115200 skips esptool's post-stub baud switch,
|
||||
; one less step to fail on a marginal USB link.
|
||||
upload_speed = 115200
|
||||
build_flags = ${esp32_base.build_flags}
|
||||
${sensor_base.build_flags}
|
||||
-I variants/wio-tracker-l2
|
||||
-D SEEED_WIO_TRACKER_L2
|
||||
-D PIN_BOARD_SDA=47
|
||||
-D PIN_BOARD_SCL=48
|
||||
-D PIN_USER_BTN=0
|
||||
-D USE_SX1262
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D P_LORA_NSS=21
|
||||
-D P_LORA_RESET=7
|
||||
-D P_LORA_BUSY=8
|
||||
-D P_LORA_DIO_1=9
|
||||
-D P_LORA_SCLK=4
|
||||
-D P_LORA_MISO=5
|
||||
-D P_LORA_MOSI=6
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D PIN_GPS_TX=18 ; L76K TX -> ESP32 RX
|
||||
-D PIN_GPS_RX=17 ; L76K RX <- ESP32 TX
|
||||
-D GPS_BAUD_RATE=9600
|
||||
-D ENV_SKIP_GPS_DETECT=1 ; GPS is always present, powered via IO expander
|
||||
; disable ALL env sensor I2C probes: this bus carries six onboard chips
|
||||
; (TCA9535 0x21, AW35615 0x22, LP5814 0x2C, ADS1115 0x48, GT911 0x5D,
|
||||
; ES8311 0x18) and stray probe matches can hang sensors.begin()
|
||||
-D ENV_INCLUDE_AHTX0=0
|
||||
-D ENV_INCLUDE_BME280=0
|
||||
-D ENV_INCLUDE_BMP280=0
|
||||
-D ENV_INCLUDE_SHTC3=0
|
||||
-D ENV_INCLUDE_SHT4X=0
|
||||
-D ENV_INCLUDE_INA3221=0
|
||||
-D ENV_INCLUDE_INA219=0
|
||||
-D ENV_INCLUDE_INA226=0
|
||||
-D ENV_INCLUDE_INA260=0
|
||||
-D ENV_INCLUDE_MLX90614=0
|
||||
-D ENV_INCLUDE_VL53L0X=0
|
||||
-D ENV_INCLUDE_BME680=0
|
||||
-D ENV_INCLUDE_BMP085=0
|
||||
-D ENV_INCLUDE_LPS22HB=0
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/wio-tracker-l2>
|
||||
+<helpers/sensors/*.cpp>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
${sensor_base.lib_deps}
|
||||
lovyan03/LovyanGFX @ ^1.2.30 ; >=1.2.30 defines Panel_NV3031B::init_cmds itself (PR #3381 needed an ODR fix for older)
|
||||
|
||||
[env:Wio_Tracker_L2_repeater]
|
||||
extends = Wio_Tracker_L2
|
||||
build_flags =
|
||||
${Wio_Tracker_L2.build_flags}
|
||||
-D ADVERT_NAME='"WioL2 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
build_src_filter = ${Wio_Tracker_L2.build_src_filter}
|
||||
+<../examples/simple_repeater/*.cpp>
|
||||
lib_deps =
|
||||
${Wio_Tracker_L2.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
|
||||
WioTrackerL2Board board;
|
||||
|
||||
static SPIClass spi;
|
||||
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);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
MicroNMEALocationProvider gps(Serial1, &rtc_clock);
|
||||
EnvironmentSensorManager sensors(gps);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
DISPLAY_CLASS display;
|
||||
MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true);
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
MESH_DEBUG_PRINTLN("radio_init: rtc + sx1262 init");
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire); // Wire already running on 47/48 from board.begin()
|
||||
|
||||
bool ok = radio.std_init(&spi);
|
||||
MESH_DEBUG_PRINTLN("radio_init: SX1262 %s", ok ? "OK" : "FAILED");
|
||||
return ok;
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng); // create new random identity
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include "WioTrackerL2Board.h"
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include "WioTrackerL2Display.h"
|
||||
#include <helpers/ui/MomentaryButton.h>
|
||||
#endif
|
||||
|
||||
extern WioTrackerL2Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern DISPLAY_CLASS display;
|
||||
extern MomentaryButton user_btn;
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
Reference in New Issue
Block a user