Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/helpers/ethernet/EthernetInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/ethernet/SerialEthernetInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 43 additions & 0 deletions src/helpers/ethernet/lan8720/LAN8720EthernetInterface.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
23 changes: 23 additions & 0 deletions src/helpers/ethernet/lan8720/LAN8720EthernetInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "../SerialEthernetInterface.h"
#include <ETH.h>
#include <WiFi.h>

// 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); }
};
116 changes: 116 additions & 0 deletions variants/wt32_eth01/README.md
Original file line number Diff line number Diff line change
@@ -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/<environment name>/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.
57 changes: 57 additions & 0 deletions variants/wt32_eth01/platformio.ini
Original file line number Diff line number Diff line change
@@ -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}
+<helpers/ethernet/SerialEthernetInterface.cpp>
+<helpers/ethernet/lan8720/*.cpp>

[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}
+<helpers/esp32/*.cpp>
18 changes: 18 additions & 0 deletions variants/wt32_eth01/target.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
24 changes: 24 additions & 0 deletions variants/wt32_eth01/target.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <RadioLib.h>
#include <helpers/ESP32Board.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/radiolib/CustomSX1262Wrapper.h>
#include <helpers/SensorManager.h>

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();