diff --git a/src/helpers/ethernet/EthernetInterface.h b/src/helpers/ethernet/EthernetInterface.h index 4e1f9176a6a..b2186a37966 100644 --- a/src/helpers/ethernet/EthernetInterface.h +++ b/src/helpers/ethernet/EthernetInterface.h @@ -5,6 +5,8 @@ #include "helpers/ethernet/ch390/CH390EthernetInterface.h" #elif defined(ETHERNET_USE_RAK13800) #include "helpers/ethernet/RAK13800/RAK13800EthernetInterface.h" + #elif defined(ETHERNET_USE_LAN8720) + #include "helpers/ethernet/lan8720/LAN8720EthernetInterface.h" #else #error "ETHERNET_ENABLED is defined, but no specific driver flag (e.g. ETHERNET_USE_CH390) was provided!" #endif diff --git a/src/helpers/ethernet/SerialEthernetInterface.h b/src/helpers/ethernet/SerialEthernetInterface.h index 46789f71064..a5fac25df75 100644 --- a/src/helpers/ethernet/SerialEthernetInterface.h +++ b/src/helpers/ethernet/SerialEthernetInterface.h @@ -51,7 +51,7 @@ class SerialEthernetInterface : public BaseSerialInterface { void disable() override; bool isEnabled() const override { return _isEnabled; } - bool isConnected() const override; + bool isConnected() const override = 0; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; diff --git a/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.cpp b/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.cpp new file mode 100644 index 00000000000..c5ffc55bbcb --- /dev/null +++ b/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.cpp @@ -0,0 +1,43 @@ +#include "LAN8720EthernetInterface.h" + +bool LAN8720EthernetInterface::begin() { + initialized = ETH.begin(); + if (!initialized) { + Serial.println("ERROR: LAN8720 init failed"); + } + return initialized; +} + +void LAN8720EthernetInterface::loop() { + if (!initialized) return; + + IPAddress current = ETH.localIP(); + bool ready = ETH.linkUp() && current != IPAddress(0, 0, 0, 0); + if (listening && (!ready || current != address)) { + client.stop(); + server.end(); + connected = false; + listening = false; + } + if (!ready) return; + + if (!listening) { + address = current; + server.begin(ETHERNET_TCP_PORT); + listening = true; + ETHERNET_DEBUG_PRINTLN("Listening on %s:%d", address.toString().c_str(), ETHERNET_TCP_PORT); + } + + auto incoming = server.available(); + if (incoming) { + client.stop(); + client = incoming; + // Reset queued replies as well as partial input from the previous client. + bool enabled = isEnabled(); + SerialEthernetInterface::disable(); + SerialEthernetInterface::enable(); + if (!enabled) SerialEthernetInterface::disable(); + onClientConnected(); + } + connected = client.connected(); +} diff --git a/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.h b/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.h new file mode 100644 index 00000000000..bac52bc0866 --- /dev/null +++ b/src/helpers/ethernet/lan8720/LAN8720EthernetInterface.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../SerialEthernetInterface.h" +#include +#include + +// Uses the PHY configuration supplied by the Arduino board variant. +class LAN8720EthernetInterface : public SerialEthernetInterface { + WiFiServer server; + WiFiClient client; + bool initialized = false; + bool listening = false; + bool connected = false; + IPAddress address; + +public: + bool begin(); + void loop() override; + bool isConnected() const override { return connected; } + int available() override { return client.available(); } + int read() override { return client.read(); } + size_t write(const uint8_t* buf, size_t size) override { return client.write(buf, size); } +}; diff --git a/variants/wt32_eth01/README.md b/variants/wt32_eth01/README.md new file mode 100644 index 00000000000..5def9e6ae11 --- /dev/null +++ b/variants/wt32_eth01/README.md @@ -0,0 +1,116 @@ +# WT32-ETH01 + Heltec HT-RA62 + +MeshCore companion firmware for the original WT32-ETH01 (ESP32, LAN8720, 4 MB flash), +with an HT-RA62 SX1262 LoRa module. Not suitable for the WT32-ETH01-EVO. +The default settings in this repository use 869.618 MHz and require the **HT-RA62-HF** +(863–928 MHz); the LF version requires different radio settings. + +## Wiring + +GPIO numbers are not the physical pin numbers on the WT32 connector. + +| HT-RA62 module pin | WT32-ETH01 | +| --- | --- | +| 3 — 3V3 | 3V3 | +| 2, 9, 16 — GND | Common GND | +| 12 — SCK | GPIO14 / IO14 | +| 14 — MOSI | GPIO32 / CFG | +| 13 — MISO | GPIO35 / IO35 | +| 15 — NSS | GPIO33 / 485_EN | +| 4 — RST | GPIO4 / IO4 | +| 10 — BUSY | GPIO36 / IO36 | +| 6 — DIO1 | GPIO39 / IO39 | +| 5 — TXEN, 7 — DIO2, 8 — DIO3, 11 — RXEN | Leave externally unconnected; see below | +| 1 — ANT or IPEX connector | Antenna for the module's frequency band | + +The standard Heltec schematic already connects DIO2 to the RF control through R6 and +DIO3 to the oscillator power supply. The firmware enables RF control +on DIO2 and configures DIO3 to 1.8 V; verify this voltage if your module +has a different oscillator or hardware revision. Do not connect TXEN to an ESP32 output +in parallel with DIO2. No external jumpers are required on the standard module. + +GPIO35/36/39 are used only as inputs. This mapping leaves the boot strapping pins +and UART0 available, and does not use the GPIOs reserved for the LAN8720 +(0, 16, 18, 19, 21, 22, 23, 25, 26, 27). I2C is disabled because its +default pins, 32/33, are assigned to the radio. No display, +GPS, or battery measurement is configured. + +Power the WT32 from a stable 5 V supply and the LoRa module at +**3.3 V**, with a common ground; never apply 5 V to the signal pins or the HT-RA62 module. +Size the power supply for both boards (at least 500 mA for the +WT32 alone according to the manufacturer, with additional capacity for the radio). +Use short SPI wires and decoupling capacitors close to the module (100 nF and 10 µF). +Connect the antenna before transmitting. + +## Building + +From the repository root, with PlatformIO Core installed: + +```sh +pio run -e WT32_ETH01_HT_RA62_companion_radio_ethernet -t mergebin +pio run -e WT32_ETH01_HT_RA62_companion_radio_ble -t mergebin +pio run -e WT32_ETH01_HT_RA62_companion_radio_usb -t mergebin +``` + +If `pio` is not in your PATH, it is available on this machine at +`/home/fabio/.platformio/penv/bin/pio`. +Select one of the three versions; each command generates +`.pio/build//firmware.bin` and `firmware-merged.bin`. +The merged file includes the bootloader and partition table and is flashed at offset +`0x0`; `firmware.bin` contains only the application and is flashed at `0x10000`. +The BLE environment uses `min_spiffs.csv` to leave room for the Bluetooth stack. +Switching between BLE and the other environments changes the partition table: back up +your identity and data first, and use the full image when switching. + +## Flashing + +The WT32-ETH01 has no built-in USB interface. A USB-to-UART adapter with +**3.3 V logic levels** is required: + +| USB-UART | WT32 programming connector | +| --- | --- | +| TX | RX0 / GPIO3 | +| RX | TX0 / GPIO1 | +| GND | GND | + +Do not confuse TX0/RX0 with TXD/RXD (GPIO17/5). +Power the board separately if the adapter cannot supply enough current. +Connect GPIO0 to GND and reset or power on the board to enter the bootloader: + +```sh +pio run -e WT32_ETH01_HT_RA62_companion_radio_ethernet -t upload --upload-port /dev/ttyUSB0 +``` + +Remove the GPIO0–GND connection and reset the board after flashing. +Replace the environment and serial port as needed. + +## Connection and hardware checks + +- **Ethernet:** connect the cable to a network with DHCP. The serial monitor at + 115200 baud shows the IP address and TCP port **5000** after radio initialization. + Use a client compatible with the MeshCore companion TCP protocol; + this is not an HTTP server. No Wi-Fi SSID or password is required. The transport + does not add authentication or TLS: use it on a trusted LAN. +- **BLE:** pair the companion through the app; the initial PIN is **123456**. +- **USB:** the companion protocol runs over the UART0 adapter at 115200 baud. + Close the serial monitor before opening the client. + +The initial preset uses 869.618 MHz, BW 62.5 kHz, SF8, and 8 dBm transmit power. +Set your network's parameters through the client; saved settings +may override the build-time defaults. + +Building the firmware does not verify the physical circuit. On first boot, check +that `ERROR: radio init failed` does not appear, then test transmission and reception with a +known working node. For Ethernet, also test disconnecting and reconnecting the cable +and reconnecting the TCP client. If radio initialization fails, the companion +stops before starting the network interface. + +## Hardware references + +- [Heltec HT-RA62 datasheet](https://resource.heltec.cn/download/HT-RA62/HT-RA62%28Rev1.1%29.pdf) +- [Heltec module schematic](https://resource.heltec.cn/download/HT-RA62/HT-RA62_Schematic_diagram.pdf) +- [Heltec reference design](https://resource.heltec.cn/download/HT-RA62/HT-RA62_Reference_design.pdf) +- [Wireless-Tag WT32-ETH01 datasheet](https://www.tme.eu/Document/1503f6f34f86647f395694ec5f8ce17b/WT32-ETH01_datasheet_V1.3-en.pdf) +- The PHY configuration comes from the Arduino core's `wt32-eth01` variant: + LAN8720 address 1, power/clock-enable GPIO16, MDC GPIO23, MDIO GPIO18, + RMII clock input on GPIO0. diff --git a/variants/wt32_eth01/platformio.ini b/variants/wt32_eth01/platformio.ini new file mode 100644 index 00000000000..287db83a291 --- /dev/null +++ b/variants/wt32_eth01/platformio.ini @@ -0,0 +1,57 @@ +[WT32_ETH01_HT_RA62] +extends = esp32_base +board = wt32-eth01 +build_flags = + ${esp32_base.build_flags} + -I variants/wt32_eth01 + -D P_LORA_SCLK=14 + -D P_LORA_MOSI=32 + -D P_LORA_MISO=35 + -D P_LORA_NSS=33 + -D P_LORA_RESET=4 + -D P_LORA_BUSY=36 + -D P_LORA_DIO_1=39 + ; Do not start I2C on GPIO32/33: they belong to LoRa. + -D PIN_BOARD_SDA=-1 + -D PIN_BOARD_SCL=-1 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=8 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/wt32_eth01> + +<../examples/companion_radio/*.cpp> +lib_deps = + ${esp32_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:WT32_ETH01_HT_RA62_companion_radio_ethernet] +extends = WT32_ETH01_HT_RA62 +build_flags = + ${WT32_ETH01_HT_RA62.build_flags} + -D ETHERNET_ENABLED=1 + -D ETHERNET_USE_LAN8720=1 + -D ETHERNET_CLASS=LAN8720EthernetInterface + -D ETHERNET_DEBUG_LOGGING=1 +build_src_filter = ${WT32_ETH01_HT_RA62.build_src_filter} + + + + + +[env:WT32_ETH01_HT_RA62_companion_radio_usb] +extends = WT32_ETH01_HT_RA62 +build_flags = + ${WT32_ETH01_HT_RA62.build_flags} + -D ENABLE_USB_INTERFACE + +[env:WT32_ETH01_HT_RA62_companion_radio_ble] +extends = WT32_ETH01_HT_RA62 +board_build.partitions = min_spiffs.csv +build_flags = + ${WT32_ETH01_HT_RA62.build_flags} + -D BLE_PIN_CODE=123456 +build_src_filter = ${WT32_ETH01_HT_RA62.build_src_filter} + + diff --git a/variants/wt32_eth01/target.cpp b/variants/wt32_eth01/target.cpp new file mode 100644 index 00000000000..74d1878224b --- /dev/null +++ b/variants/wt32_eth01/target.cpp @@ -0,0 +1,18 @@ +#include "target.h" + +WT32ETH01Board 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 rtc_clock; +SensorManager sensors; + +bool radio_init() { + rtc_clock.begin(); + return radio.std_init(&spi); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/wt32_eth01/target.h b/variants/wt32_eth01/target.h new file mode 100644 index 00000000000..f54378d6d62 --- /dev/null +++ b/variants/wt32_eth01/target.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +#include + +class WT32ETH01Board : public ESP32Board { +public: + void begin() { + ESP32Board::begin(); + setInhibitSleep(true); + } + const char* getManufacturerName() const override { return "WT32-ETH01 + HT-RA62"; } +}; + +extern WT32ETH01Board board; +extern WRAPPER_CLASS radio_driver; +extern ESP32RTCClock rtc_clock; +extern SensorManager sensors; + +bool radio_init(); +mesh::LocalIdentity radio_new_identity();