mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
build: add the ESP32-P4 (#29)
For boards such as Waveshare's ESP32-P4-WIFI6-POE-ETH: WiFi through the board's ESP32-C6 on SDIO (esp-hosted), Ethernet on the P4's RMII MAC with an IP101. Not tested on a device: no P4 board was at hand. - platformio.ini: env esp32p4 (esp32-p4_r3, chip v3.01+) and esp32p4es (esp32-p4, the esp32p4_es libraries, chips before v2.0). An image runs on one of the two only, so each has its own release files, chip models "p4" and "p4es". Both keep the OTA slot: at level 2 they leave about 128 KB of it. - Ethernet clock mode: stored as eth_clock_id_t in the ESP32's numbering and translated by name, like the PHY id. The P4's eth_clock_mode_t is the IDF's emac_rmii_clock_mode_t, whose 0 is a deprecated default; only clock in (GPIO 50) translates there. - P4 Ethernet defaults: the Waveshare wiring (IP101 as TLK110, address 1, MDC 31, MDIO 52, reset 51), board type 8. The ESP32's put MDIO on an SDIO pin. - Settings take pins up to GPIO 54, the P4's last, instead of 48. - Radio defaults: SPI 32/33/27, CSN 46 (the framework's Waveshare variant), TX 2, RX 3. - Web UI: a Waveshare ESP32-P4-ETH preset, the P4's clock mode, pin lists up to GPIO 54 without the pins the C6 link, Ethernet, USB and UART0 use. - CI builds and releases both, with their ELFs; bootloader at 0x2000. - ADR 0003: amended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
eadd9b801d
commit
dfb003d2f7
19 changed files with 282 additions and 38 deletions
2
.github/ISSUE_TEMPLATE/BUG-REPORT.yaml
vendored
2
.github/ISSUE_TEMPLATE/BUG-REPORT.yaml
vendored
|
|
@ -72,7 +72,7 @@ body:
|
|||
> After a crash, include the whole panic: the `Guru Meditation Error` line, the `Backtrace:` line and the `ELF file SHA256:` line.
|
||||
> Each release has a `SomfyController.ino.<chip>.elf.zip` that decodes the backtrace:
|
||||
> `xtensa-esp32s3-elf-addr2line -pfiaC -e SomfyController.ino.esp32s3.elf <backtrace addresses>`
|
||||
> (`riscv32-esp-elf-addr2line` on the C3, C5 and C6).
|
||||
> (`riscv32-esp-elf-addr2line` on the C3, C5, C6 and P4).
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
20
.github/workflows/build.yaml
vendored
20
.github/workflows/build.yaml
vendored
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
# elfname: the symbols of that binary, to decode a crash's backtrace
|
||||
# obname: onboard image (bootloader + partitions + firmware + littlefs merged)
|
||||
# for flashing a new chip via USB/serial
|
||||
# addr_bootloader: chip-dependent (ESP32: 0x1000, S3/C3/C6: 0x0, C5: 0x2000)
|
||||
# addr_bootloader: chip-dependent (ESP32: 0x1000, S3/C3/C6: 0x0, C5/P4: 0x2000)
|
||||
# addr_fs: must match the spiffs offset in the env's partition table
|
||||
# (0x310000 in both esp32_3MB.csv and esp32_huge_app.csv)
|
||||
include:
|
||||
|
|
@ -110,6 +110,24 @@ jobs:
|
|||
elfname: SomfyController.ino.esp32c6.elf
|
||||
fsname: SomfyController.littlefs.esp32c6.bin
|
||||
obname: SomfyController.onboard.esp32c6.bin
|
||||
- env: esp32p4
|
||||
name: ESP32-P4
|
||||
chip: ESP32-P4
|
||||
addr_bootloader: "0x2000"
|
||||
addr_fs: "0x310000"
|
||||
fwname: SomfyController.ino.esp32p4.bin
|
||||
elfname: SomfyController.ino.esp32p4.elf
|
||||
fsname: SomfyController.littlefs.esp32p4.bin
|
||||
obname: SomfyController.onboard.esp32p4.bin
|
||||
- env: esp32p4es
|
||||
name: ESP32-P4 (before v3.0)
|
||||
chip: ESP32-P4
|
||||
addr_bootloader: "0x2000"
|
||||
addr_fs: "0x310000"
|
||||
fwname: SomfyController.ino.esp32p4es.bin
|
||||
elfname: SomfyController.ino.esp32p4es.elf
|
||||
fsname: SomfyController.littlefs.esp32p4es.bin
|
||||
obname: SomfyController.onboard.esp32p4es.bin
|
||||
|
||||
steps:
|
||||
- name: Get Release
|
||||
|
|
|
|||
|
|
@ -1578,23 +1578,22 @@ class Wifi {
|
|||
{ val: 4, label: 'T-Internet POE - LILYGO', clk: 3, ct: 0, addr: 0, pwr: 16, mdc: 23, mdio: 18 },
|
||||
{ val: 5, label: 'wESP32 v7+ - Silicognition', clk: 0, ct: 2, addr: 0, pwr: -1, mdc: 16, mdio: 17 },
|
||||
{ val: 6, label: 'wESP32 < v7 - Silicognition', clk: 0, ct: 0, addr: 0, pwr: -1, mdc: 16, mdio: 17 },
|
||||
{ val: 1, label: 'WT32-ETH01 - Wireless Tag', clk: 0, ct: 0, addr: 1, pwr: 16, mdc: 23, mdio: 18 }
|
||||
{ val: 1, label: 'WT32-ETH01 - Wireless Tag', clk: 0, ct: 0, addr: 1, pwr: 16, mdc: 23, mdio: 18 },
|
||||
{ val: 8, label: 'ESP32-P4-ETH - Waveshare', clk: 0, ct: 1, addr: 1, pwr: 51, mdc: 31, mdio: 52, p4: true }
|
||||
];
|
||||
ethClockModes = [{ val: 0, label: 'GPIO0 IN' }, { val: 1, label: 'GPIO0 OUT' }, { val: 2, label: 'GPIO16 OUT' }, { val: 3, label: 'GPIO17 OUT' }];
|
||||
// The P4 takes the clock in on GPIO 50 only (eth_clock_id_t in EthPhy.h).
|
||||
ethClockModesP4 = [{ val: 0, label: 'GPIO50 IN' }];
|
||||
// The ids the firmware stores (eth_phy_id_t in EthPhy.h). 4 is the DM9051, an SPI chip the ESP32's MAC can't drive.
|
||||
ethPhyTypes = [{ val: 0, label: 'LAN8720' }, { val: 1, label: 'TLK110' }, { val: 2, label: 'RTL8201' }, { val: 3, label: 'DP83848' }, { val: 5, label: 'KSZ8041' }, { val: 6, label: 'KSZ8081' }];
|
||||
init() {
|
||||
document.getElementById("divNetworkStrength").innerHTML = this.displaySignal(-100);
|
||||
if (this.initialized) return;
|
||||
let addr = [];
|
||||
this.loadETHDropdown(document.getElementById('selETHClkMode'), this.ethClockModes);
|
||||
this.loadETHDropdown(document.getElementById('selETHPhyType'), this.ethPhyTypes);
|
||||
this.loadETHDropdown(document.getElementById('selETHBoardType'), this.ethBoardTypes);
|
||||
for (let i = 0; i < 32; i++) addr.push({ val: i, label: `PHY ${i}` });
|
||||
this.loadETHDropdown(document.getElementById('selETHAddress'), addr);
|
||||
this.loadETHPins(document.getElementById('selETHPWRPin'), 'power');
|
||||
this.loadETHPins(document.getElementById('selETHMDCPin'), 'mdc', 23);
|
||||
this.loadETHPins(document.getElementById('selETHMDIOPin'), 'mdio', 18);
|
||||
this.loadETHOptions();
|
||||
ui.toElement(document.getElementById('divNetAdapter'), {
|
||||
wifi: {ssid:'', passphrase:''},
|
||||
ethernet: { boardType: 1, wirelessFallback: false, dhcp: true, dns1: '', dns2: '', ip: '', gateway: '' }
|
||||
|
|
@ -1602,14 +1601,27 @@ class Wifi {
|
|||
this.onETHBoardTypeChanged(document.getElementById('selETHBoardType'));
|
||||
this.initialized = true;
|
||||
}
|
||||
loadETHPins(sel, type, selected) {
|
||||
isP4() { return (document.getElementById('divContainer').getAttribute('data-chipmodel') || '').startsWith('p4'); }
|
||||
clockModes() { return this.isP4() ? this.ethClockModesP4 : this.ethClockModes; }
|
||||
// The board presets, clock modes and pins of the chip: the ESP32's or the
|
||||
// P4's. The chip comes with the sync, after init().
|
||||
loadETHOptions() {
|
||||
let p4 = this.isP4();
|
||||
this.loadETHDropdown(document.getElementById('selETHClkMode'), this.clockModes());
|
||||
this.loadETHDropdown(document.getElementById('selETHBoardType'), this.ethBoardTypes.filter(b => b.val === 0 || (b.p4 === true) === p4));
|
||||
let maxPin = p4 ? 54 : 35;
|
||||
this.loadETHPins(document.getElementById('selETHPWRPin'), 'power', undefined, maxPin);
|
||||
this.loadETHPins(document.getElementById('selETHMDCPin'), 'mdc', p4 ? 31 : 23, maxPin);
|
||||
this.loadETHPins(document.getElementById('selETHMDIOPin'), 'mdio', p4 ? 52 : 18, maxPin);
|
||||
}
|
||||
loadETHPins(sel, type, selected, maxPin) {
|
||||
let arr = [];
|
||||
switch (type) {
|
||||
case 'power':
|
||||
arr.push({ val: -1, label: 'None' });
|
||||
break;
|
||||
}
|
||||
for (let i = 0; i < 36; i++) {
|
||||
for (let i = 0; i <= maxPin; i++) {
|
||||
arr.push({ val: i, label: `GPIO ${i}` });
|
||||
}
|
||||
this.loadETHDropdown(sel, arr, selected);
|
||||
|
|
@ -1652,6 +1664,7 @@ class Wifi {
|
|||
else {
|
||||
let settings = ProtoViews.network(proto.schema, reply);
|
||||
console.log(settings);
|
||||
this.loadETHOptions();
|
||||
let hasEthernet = settings.hasEthernet;
|
||||
this.hasEthernet = hasEthernet;
|
||||
document.getElementById('divHardwired').style.display = hasEthernet ? '' : 'none';
|
||||
|
|
@ -1815,7 +1828,7 @@ class Wifi {
|
|||
if (obj.connType >= 2) {
|
||||
let boardType = this.ethBoardTypes.find(elem => obj.ethernet.boardType === elem.val);
|
||||
let phyType = this.ethPhyTypes.find(elem => obj.ethernet.phyType === elem.val);
|
||||
let clkMode = this.ethClockModes.find(elem => obj.ethernet.CLKMode === elem.val);
|
||||
let clkMode = this.clockModes().find(elem => obj.ethernet.CLKMode === elem.val);
|
||||
let div = document.createElement('div');
|
||||
let html = `<div id="divLanSettings" class="inst-overlay">`;
|
||||
html += '<div style="width:100%;color:red;text-align:center;font-weight:bold;"><span style="padding:10px;display:inline-block;width:100%;border-radius:5px;border-top-right-radius:17px;border-top-left-radius:17px;background:white;">BEWARE ... WARNING ... DANGER<span></div>';
|
||||
|
|
@ -2720,12 +2733,17 @@ class Somfy {
|
|||
{ name: 's3', maxPins: 48, inputs: [19, 20, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32], outputs: [19, 20, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32] },
|
||||
{ name: 'c3', maxPins: 21, inputs: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], outputs: [11, 12, 13, 14, 15, 16, 17, 21] },
|
||||
{ name: 'c5', maxPins: 28, inputs: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22], outputs: [13, 14, 15, 16, 17, 18, 19, 20, 21, 22] },
|
||||
{ name: 'c6', maxPins: 23, inputs: [], outputs: [] }
|
||||
{ name: 'c6', maxPins: 23, inputs: [], outputs: [] },
|
||||
// Without the C6's SDIO link (14-19, 54), USB Serial/JTAG (24, 25),
|
||||
// Ethernet (28-31, 34, 35, 49-52) and UART0 (37, 38).
|
||||
{ name: 'p4', maxPins: 54, inputs: [14, 15, 16, 17, 18, 19, 24, 25, 28, 29, 30, 31, 34, 35, 37, 38, 49, 50, 51, 52, 54], outputs: [14, 15, 16, 17, 18, 19, 24, 25, 28, 29, 30, 31, 34, 35, 37, 38, 49, 50, 51, 52, 54] }
|
||||
];
|
||||
|
||||
loadPins(type, sel, opt) {
|
||||
while (sel.firstChild) sel.removeChild(sel.firstChild);
|
||||
let cm = document.getElementById('divContainer').getAttribute('data-chipmodel');
|
||||
// The pre-v3 P4 build has the same pins.
|
||||
if (cm === 'p4es') cm = 'p4';
|
||||
let pm = this.pinMaps.find(x => x.name === cm) || { name: '', maxPins: 39, inputs: [], outputs: [] };
|
||||
//console.log({ cm: cm, pm: pm });
|
||||
for (let i = 0; i <= pm.maxPins; i++) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ We moved every firmware env from `espressif32 @ 6.13.0` (Arduino 2.0.17 on ESP-I
|
|||
|
||||
> **Amended by [ADR 0006](0006-two-security-modes-eleven-http-routes.md) (4.0.0).** The C3 builds again. Without the old API (ports 8080 and 8081), its image is 1,498,512 bytes at level 2 and leaves 72.6 KB of the OTA slot. It runs on chip revision v0.3 or later only: the framework's C3 libraries are built with `CONFIG_ESP32C3_REV_MIN_3`. Its default radio pins moved off GPIO 12-17, which hold the SPI flash.
|
||||
- **The C5 is updated over USB.** New in 4.0.0. At level 2 its image left 3.4 KB of the OTA slot, under CI's 8 KB, so like the C6 it is released as a debug build with one 3 MB app slot (`esp32_huge_app.csv`), which no OTA can write. The framework's C5 libraries run on chip revision v1.0 or later only (`CONFIG_ESP32C5_REV_MIN_FULL=100`). Its WiFi has 5 GHz too, so the passive scans for the configured network (`src/ESPNetwork.cpp`, 300 ms a channel) cover about 38 channels instead of 13: about 11 s instead of 4 s, depending on the country's channels.
|
||||
- **Ethernet PHY ids are ours.** Arduino 3.x renumbered `eth_phy_type_t` (`ETH_PHY_GENERIC` is 0). NVS, backups and the web UI keep the Arduino 2.x numbers as `eth_phy_id_t` (`src/EthPhy.h`), translated by name when Ethernet starts. Never cast a stored id to the framework enum.
|
||||
- **Ethernet only on the original ESP32.** Arduino 3.x has the RMII MAC API only where `CONFIG_ETH_USE_ESP32_EMAC` is set. S3, C3, C5 and C6 build without Ethernet.
|
||||
- **The P4 has two builds.** New in 4.0.0, for boards such as Waveshare's ESP32-P4-WIFI6-POE-ETH. The framework's P4 libraries run on chip revision v3.01 or later only (`CONFIG_ESP32P4_REV_MIN_FULL=301`), its `esp32p4_es` libraries on revisions before v2.0, so `esp32p4` (chip model `p4`) and `esp32p4es` (`p4es`) each get their own release files. Both keep the OTA slot: at level 2 their images leave about 128 KB. WiFi goes through the board's ESP32-C6 on SDIO (esp-hosted, the framework's pins 14-19 and reset 54), so the C6 has to run a matching esp-hosted slave firmware, which Waveshare's boards ship with.
|
||||
- **Ethernet PHY ids are ours.** Arduino 3.x renumbered `eth_phy_type_t` (`ETH_PHY_GENERIC` is 0). NVS, backups and the web UI keep the Arduino 2.x numbers as `eth_phy_id_t` (`src/EthPhy.h`), translated by name when Ethernet starts. Never cast a stored id to the framework enum. The RMII clock mode is kept the same way, as `eth_clock_id_t` in the ESP32's numbering: the P4's `eth_clock_mode_t` is the IDF's `emac_rmii_clock_mode_t`, whose 0 is a deprecated default. On the P4 only clock in (GPIO 50) translates, since `ETH.begin()` keeps the clock on that pin.
|
||||
- **Ethernet only on the original ESP32 and the P4.** Arduino 3.x has the RMII MAC API only where `CONFIG_ETH_USE_ESP32_EMAC` is set. S3, C3, C5 and C6 build without Ethernet.
|
||||
- **Updates roll back until proven.** We override the core's `verifyRollbackLater()` hook to return true, and `loop()` marks the new firmware valid once it has run 60 s and the network is up. A reset before then boots the previous firmware. The LittleFS image an update wrote stays, so the old firmware runs with the new web UI.
|
||||
- **Tooling.** pioarduino needs its own Core fork (`pip install pioarduino`, the pioarduino IDE extension in VS Code), which it installs into `~/.platformio/penv` in place of PlatformIO Core.
|
||||
|
|
|
|||
|
|
@ -126,6 +126,26 @@ build_flags =
|
|||
${esp32.build_flags}
|
||||
-DCORE_DEBUG_LEVEL=3
|
||||
|
||||
; ESP32-P4 boards such as Waveshare's ESP32-P4-WIFI6-POE-ETH: WiFi through an
|
||||
; ESP32-C6 on SDIO (esp-hosted), Ethernet on the P4's own RMII MAC. The
|
||||
; framework's P4 libraries run on chip revision v3.01 or later only, and its
|
||||
; esp32p4_es libraries on revisions before v2.0, so each gets its own build.
|
||||
[env:esp32p4]
|
||||
extends = esp32
|
||||
board = esp32-p4_r3
|
||||
board_build.partitions = esp32_3MB.csv
|
||||
build_flags =
|
||||
${dualcore.build_flags}
|
||||
-DCORE_DEBUG_LEVEL=2
|
||||
|
||||
[env:esp32p4es]
|
||||
extends = esp32
|
||||
board = esp32-p4
|
||||
board_build.partitions = esp32_3MB.csv
|
||||
build_flags =
|
||||
${dualcore.build_flags}
|
||||
-DCORE_DEBUG_LEVEL=2
|
||||
|
||||
; Host-side unit tests: pio test -e native (needs gcc/g++ on PATH).
|
||||
; Only the sources the tests cover are built; test/stubs stands in for Arduino.h.
|
||||
[env:native]
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ static net_record_t netRecord() {
|
|||
#if HAS_ETHERNET
|
||||
rec.ethBoardType = settings.Ethernet.boardType;
|
||||
rec.ethPhyType = settings.Ethernet.phyType;
|
||||
rec.ethClkMode = static_cast<uint8_t>(settings.Ethernet.CLKMode);
|
||||
rec.ethClkMode = settings.Ethernet.CLKMode;
|
||||
rec.ethPhyAddress = settings.Ethernet.phyAddress;
|
||||
rec.ethPWRPin = settings.Ethernet.PWRPin;
|
||||
rec.ethMDCPin = settings.Ethernet.MDCPin;
|
||||
|
|
@ -196,7 +196,7 @@ static void applyNetRecord(const net_record_t &rec, const restore_options_t &opt
|
|||
ESP_LOGI(TAG, "Restoring Ethernet adapter settings");
|
||||
settings.Ethernet.boardType = rec.ethBoardType;
|
||||
settings.Ethernet.phyType = rec.ethPhyType;
|
||||
settings.Ethernet.CLKMode = static_cast<eth_clock_mode_t>(rec.ethClkMode);
|
||||
settings.Ethernet.CLKMode = rec.ethClkMode;
|
||||
settings.Ethernet.phyAddress = rec.ethPhyAddress;
|
||||
settings.Ethernet.PWRPin = rec.ethPWRPin;
|
||||
settings.Ethernet.MDCPin = rec.ethMDCPin;
|
||||
|
|
|
|||
|
|
@ -145,6 +145,17 @@ bool ConfigSettings::begin() {
|
|||
strcpy(this->chipModel, "c6");
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
// Chips before v3.0 run their own build (esp32p4_es libraries), so they
|
||||
// get their own release files.
|
||||
case esp_chip_model_t::CHIP_ESP32P4:
|
||||
#if CONFIG_ESP32P4_SELECTS_REV_LESS_V3
|
||||
strcpy(this->chipModel, "p4es");
|
||||
#else
|
||||
strcpy(this->chipModel, "p4");
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
sprintf(this->chipModel, "UNK%d", static_cast<int>(ci.model));
|
||||
break;
|
||||
|
|
@ -499,7 +510,7 @@ bool EthernetSettings::begin() {
|
|||
bool EthernetSettings::fromJSON(JsonObject &obj) {
|
||||
if(!obj["boardType"].isNull()) this->boardType = obj["boardType"];
|
||||
if(!obj["phyAddress"].isNull()) this->phyAddress = obj["phyAddress"];
|
||||
if(!obj["CLKMode"].isNull()) this->CLKMode = static_cast<eth_clock_mode_t>(obj["CLKMode"]);
|
||||
if(!obj["CLKMode"].isNull()) this->CLKMode = obj["CLKMode"];
|
||||
if(!obj["phyType"].isNull()) this->phyType = obj["phyType"];
|
||||
if(!obj["PWRPin"].isNull()) this->PWRPin = obj["PWRPin"];
|
||||
if(!obj["MDCPin"].isNull()) this->MDCPin = obj["MDCPin"];
|
||||
|
|
@ -507,9 +518,7 @@ bool EthernetSettings::fromJSON(JsonObject &obj) {
|
|||
return true;
|
||||
}
|
||||
bool EthernetSettings::usesPin(uint8_t pin) {
|
||||
if((this->CLKMode == 0 || this->CLKMode == 1) && pin == 0) return true;
|
||||
else if(this->CLKMode == 2 && pin == 16) return true;
|
||||
else if(this->CLKMode == 3 && pin == 17) return true;
|
||||
if(ethClockPin(this->CLKMode) == pin) return true;
|
||||
else if(this->PWRPin == pin) return true;
|
||||
else if(this->MDCPin == pin) return true;
|
||||
else if(this->MDIOPin == pin) return true;
|
||||
|
|
@ -522,7 +531,7 @@ bool EthernetSettings::save() {
|
|||
pref.putChar("boardType", this->boardType);
|
||||
pref.putChar("phyAddress", this->phyAddress);
|
||||
pref.putChar("phyType", this->phyType);
|
||||
pref.putChar("CLKMode", static_cast<uint8_t>(this->CLKMode));
|
||||
pref.putChar("CLKMode", this->CLKMode);
|
||||
pref.putChar("PWRPin", this->PWRPin);
|
||||
pref.putChar("MDCPin", this->MDCPin);
|
||||
pref.putChar("MDIOPin", this->MDIOPin);
|
||||
|
|
@ -534,7 +543,7 @@ bool EthernetSettings::load() {
|
|||
pref.begin("ETH");
|
||||
this->boardType = pref.getChar("boardType", this->boardType);
|
||||
this->phyType = pref.getChar("phyType", this->phyType);
|
||||
this->CLKMode = static_cast<eth_clock_mode_t>(pref.getChar("CLKMode", ETH_CLOCK_GPIO0_IN));
|
||||
this->CLKMode = pref.getChar("CLKMode", this->CLKMode);
|
||||
this->phyAddress = pref.getChar("phyAddress", this->phyAddress);
|
||||
this->PWRPin = pref.getChar("PWRPin", this->PWRPin);
|
||||
this->MDCPin = pref.getChar("MDCPin", this->MDCPin);
|
||||
|
|
|
|||
|
|
@ -88,14 +88,26 @@ class WifiSettings: BaseSettings {
|
|||
class EthernetSettings: BaseSettings {
|
||||
public:
|
||||
EthernetSettings();
|
||||
uint8_t CLKMode = static_cast<uint8_t>(eth_clock_id_t::GPIO0_IN); // An eth_clock_id_t, see EthPhy.h.
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
// The IP101 wiring of Espressif's P4 boards, which Waveshare's ESP32-P4-ETH
|
||||
// boards copy, and the web UI's board type for it. The ESP32's defaults
|
||||
// would put MDIO on an SDIO pin.
|
||||
uint8_t boardType = 8;
|
||||
uint8_t phyType = static_cast<uint8_t>(eth_phy_id_t::TLK110);
|
||||
int8_t phyAddress = 1;
|
||||
int8_t PWRPin = 51;
|
||||
int8_t MDCPin = 31;
|
||||
int8_t MDIOPin = 52;
|
||||
#else
|
||||
uint8_t boardType = 0; // These board types are enumerated in the ui and used to set the chip settings.
|
||||
uint8_t phyType = static_cast<uint8_t>(eth_phy_id_t::LAN8720); // An eth_phy_id_t, see EthPhy.h.
|
||||
eth_clock_mode_t CLKMode = ETH_CLOCK_GPIO0_IN;
|
||||
// The LAN8720 wiring Arduino 2.x defaulted to. 3.x has no defaults.
|
||||
int8_t phyAddress = 0;
|
||||
int8_t PWRPin = -1;
|
||||
int8_t MDCPin = 23;
|
||||
int8_t MDIOPin = 18;
|
||||
#endif
|
||||
|
||||
bool begin();
|
||||
bool fromJSON(JsonObject &obj);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ static void writeSettings(ProtoWriter &w, settings_section_t section) {
|
|||
w.add(proto_field_t::hasEthernet, true);
|
||||
w.add(proto_field_t::boardType, settings.Ethernet.boardType);
|
||||
w.add(proto_field_t::phyType, settings.Ethernet.phyType);
|
||||
w.add(proto_field_t::CLKMode, static_cast<uint8_t>(settings.Ethernet.CLKMode));
|
||||
w.add(proto_field_t::CLKMode, settings.Ethernet.CLKMode);
|
||||
w.add(proto_field_t::phyAddress, settings.Ethernet.phyAddress);
|
||||
w.add(proto_field_t::PWRPin, settings.Ethernet.PWRPin);
|
||||
w.add(proto_field_t::MDCPin, settings.Ethernet.MDCPin);
|
||||
|
|
|
|||
|
|
@ -356,11 +356,14 @@ bool ESPNetwork::connectWired() {
|
|||
ETH.setHostname("ESPSomfy-RTS");
|
||||
ESP_LOGD(TAG, "Set hostname to: %s", ETH.getHostname());
|
||||
eth_phy_type_t phyType;
|
||||
eth_clock_mode_t clkMode;
|
||||
bool begun = false;
|
||||
if(!ethPhyType(settings.Ethernet.phyType, phyType))
|
||||
ESP_LOGE(TAG, "Ethernet PHY type %u is not supported", (unsigned int)settings.Ethernet.phyType);
|
||||
else if(!ethClockMode(settings.Ethernet.CLKMode, clkMode))
|
||||
ESP_LOGE(TAG, "Ethernet clock mode %u is not supported", (unsigned int)settings.Ethernet.CLKMode);
|
||||
else
|
||||
begun = ETH.begin(phyType, settings.Ethernet.phyAddress, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.PWRPin, settings.Ethernet.CLKMode);
|
||||
begun = ETH.begin(phyType, settings.Ethernet.phyAddress, settings.Ethernet.MDCPin, settings.Ethernet.MDIOPin, settings.Ethernet.PWRPin, clkMode);
|
||||
if(!begun) {
|
||||
ESP_LOGE(TAG, "Ethernet Begin failed");
|
||||
this->ethStarted = false;
|
||||
|
|
|
|||
45
src/EthPhy.h
45
src/EthPhy.h
|
|
@ -5,8 +5,8 @@
|
|||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
// Ethernet needs the RMII MAC, which only the original ESP32 has. The C3, C5,
|
||||
// S3 and C6 build without it.
|
||||
// Ethernet needs the RMII MAC, which only the original ESP32 and the P4 have.
|
||||
// The C3, C5, S3 and C6 build without it.
|
||||
#if defined(CONFIG_ETH_USE_ESP32_EMAC) && CONFIG_ETH_USE_ESP32_EMAC
|
||||
#define HAS_ETHERNET 1
|
||||
#else
|
||||
|
|
@ -26,6 +26,32 @@ enum class eth_phy_id_t : uint8_t {
|
|||
KSZ8081 = 6
|
||||
};
|
||||
|
||||
// RMII clock ids as stored in NVS and backups and sent to the web UI: the
|
||||
// original ESP32's eth_clock_mode_t values. The P4's eth_clock_mode_t is the
|
||||
// IDF's emac_rmii_clock_mode_t, a different enum, so an id is translated.
|
||||
enum class eth_clock_id_t : uint8_t {
|
||||
GPIO0_IN = 0, // The clock comes in. On the P4, on GPIO 50.
|
||||
GPIO0_OUT = 1,
|
||||
GPIO16_OUT = 2,
|
||||
GPIO17_OUT = 3
|
||||
};
|
||||
|
||||
// The GPIO a stored clock id puts the RMII clock on, -1 for an id the chip
|
||||
// can't use. The P4's ETH.begin() takes the clock in on GPIO 50 only.
|
||||
inline int8_t ethClockPin(uint8_t id) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
return static_cast<eth_clock_id_t>(id) == eth_clock_id_t::GPIO0_IN ? 50 : -1;
|
||||
#else
|
||||
switch(static_cast<eth_clock_id_t>(id)) {
|
||||
case eth_clock_id_t::GPIO0_IN:
|
||||
case eth_clock_id_t::GPIO0_OUT: return 0;
|
||||
case eth_clock_id_t::GPIO16_OUT: return 16;
|
||||
case eth_clock_id_t::GPIO17_OUT: return 17;
|
||||
default: return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAS_ETHERNET
|
||||
#include <ETH.h>
|
||||
// Sets type to the framework's PHY for a stored id. False for an id the RMII
|
||||
|
|
@ -41,5 +67,20 @@ inline bool ethPhyType(uint8_t id, eth_phy_type_t &type) {
|
|||
default: return false;
|
||||
}
|
||||
}
|
||||
// Sets mode to the framework's clock mode for a stored id. False for an id
|
||||
// the chip can't use.
|
||||
inline bool ethClockMode(uint8_t id, eth_clock_mode_t &mode) {
|
||||
switch(static_cast<eth_clock_id_t>(id)) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
case eth_clock_id_t::GPIO0_IN: mode = EMAC_CLK_EXT_IN; return true;
|
||||
#else
|
||||
case eth_clock_id_t::GPIO0_IN: mode = ETH_CLOCK_GPIO0_IN; return true;
|
||||
case eth_clock_id_t::GPIO0_OUT: mode = ETH_CLOCK_GPIO0_OUT; return true;
|
||||
case eth_clock_id_t::GPIO16_OUT: mode = ETH_CLOCK_GPIO16_OUT; return true;
|
||||
case eth_clock_id_t::GPIO17_OUT: mode = ETH_CLOCK_GPIO17_OUT; return true;
|
||||
#endif
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -47,9 +47,10 @@ static const settings_field_t FIELDS[] = {
|
|||
{settings_part_t::ethernet, proto_field_t::phyType, nullptr, 0, 255},
|
||||
{settings_part_t::ethernet, proto_field_t::CLKMode, nullptr, 0, 3},
|
||||
{settings_part_t::ethernet, proto_field_t::phyAddress, nullptr, -1, 31},
|
||||
{settings_part_t::ethernet, proto_field_t::PWRPin, nullptr, -1, 48},
|
||||
{settings_part_t::ethernet, proto_field_t::MDCPin, nullptr, 0, 48},
|
||||
{settings_part_t::ethernet, proto_field_t::MDIOPin, nullptr, 0, 48},
|
||||
// Pins run to GPIO 54, the P4's last.
|
||||
{settings_part_t::ethernet, proto_field_t::PWRPin, nullptr, -1, 54},
|
||||
{settings_part_t::ethernet, proto_field_t::MDCPin, nullptr, 0, 54},
|
||||
{settings_part_t::ethernet, proto_field_t::MDIOPin, nullptr, 0, 54},
|
||||
{settings_part_t::ip, proto_field_t::dhcp, nullptr, 0, 0},
|
||||
{settings_part_t::ip, proto_field_t::ip, nullptr, 0, 15},
|
||||
{settings_part_t::ip, proto_field_t::gateway, nullptr, 0, 15},
|
||||
|
|
@ -75,12 +76,12 @@ static const settings_field_t FIELDS[] = {
|
|||
// RTS, RTW, RTV, GP Relay or GP Remote (radio_proto), checked on its own.
|
||||
{settings_part_t::radio, proto_field_t::proto, nullptr, 0, 9},
|
||||
{settings_part_t::radio, proto_field_t::enabled, nullptr, 0, 0},
|
||||
{settings_part_t::radio, proto_field_t::TXPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::RXPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::SCKPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::MOSIPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::MISOPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::CSNPin, nullptr, 0, 48},
|
||||
{settings_part_t::radio, proto_field_t::TXPin, nullptr, 0, 54},
|
||||
{settings_part_t::radio, proto_field_t::RXPin, nullptr, 0, 54},
|
||||
{settings_part_t::radio, proto_field_t::SCKPin, nullptr, 0, 54},
|
||||
{settings_part_t::radio, proto_field_t::MOSIPin, nullptr, 0, 54},
|
||||
{settings_part_t::radio, proto_field_t::MISOPin, nullptr, 0, 54},
|
||||
{settings_part_t::radio, proto_field_t::CSNPin, nullptr, 0, 54},
|
||||
// What the CC1101 can be set to, in MHz and kHz.
|
||||
{settings_part_t::radio, proto_field_t::frequency, nullptr, 30000, 92800},
|
||||
{settings_part_t::radio, proto_field_t::rxBandwidth, nullptr, 5803, 81250},
|
||||
|
|
|
|||
|
|
@ -506,6 +506,21 @@ void transceiver_config_t::load() {
|
|||
this->SCKPin = 6;
|
||||
this->CSNPin = 0;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
case esp_chip_model_t::CHIP_ESP32P4:
|
||||
// The SPI pins of the framework's variant for Waveshare's ESP32-P4-ETH
|
||||
// (variants/waveshare_p4_poe_eth); TX and RX take 2 and 3, which that
|
||||
// board leaves free. The C6's SDIO link holds 14-19 and 54, Ethernet
|
||||
// 28-31, 34, 35 and 49-52, and 34-38 are strapping pins. 46 is on LDO
|
||||
// channel 4, which the core turns on when a pin of it is used.
|
||||
this->TXPin = 2;
|
||||
this->RXPin = 3;
|
||||
this->MOSIPin = 32;
|
||||
this->MISOPin = 33;
|
||||
this->SCKPin = 27;
|
||||
this->CSNPin = 46;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
this->TXPin = 13;
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const char *wifiAuthModeName(uint8_t authMode);
|
|||
bool otaProven(bool networkUp, unsigned long uptimeMs, unsigned long minUptimeMs);
|
||||
// A release asset's name: "SomfyController.<kind>.esp32<chipModel>.bin", kind
|
||||
// "ino" for the firmware or "littlefs" for the web UI image, chipModel as in
|
||||
// ConfigSettings ("" for the ESP32, "s3", "c3", "c5", "c6").
|
||||
// ConfigSettings ("" for the ESP32, "s3", "c3", "c5", "c6", "p4", "p4es").
|
||||
void releaseAssetName(char *buf, size_t size, const char *kind, const char *chipModel);
|
||||
// A coredump backtrace as " 0x%08x" per frame. bt holds maxFrames entries and
|
||||
// depth is how many the dump claims are valid.
|
||||
|
|
|
|||
|
|
@ -13,4 +13,20 @@ typedef enum {
|
|||
ETH_PHY_KSZ8081,
|
||||
ETH_PHY_MAX
|
||||
} eth_phy_type_t;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
// The P4's ETH.h takes the IDF's enum, where 0 is a deprecated default.
|
||||
typedef enum {
|
||||
EMAC_CLK_DEFAULT,
|
||||
EMAC_CLK_EXT_IN,
|
||||
EMAC_CLK_OUT
|
||||
} emac_rmii_clock_mode_t;
|
||||
typedef emac_rmii_clock_mode_t eth_clock_mode_t;
|
||||
#else
|
||||
typedef enum {
|
||||
ETH_CLOCK_GPIO0_IN,
|
||||
ETH_CLOCK_GPIO0_OUT,
|
||||
ETH_CLOCK_GPIO16_OUT,
|
||||
ETH_CLOCK_GPIO17_OUT
|
||||
} eth_clock_mode_t;
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -52,6 +52,39 @@ static void test_rejects_unknown_ids() {
|
|||
TEST_ASSERT_FALSE(ethPhyType(255, type));
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Clock modes
|
||||
********************************************************************/
|
||||
static void test_clock_ids_match_stored_values() {
|
||||
TEST_ASSERT_EQUAL_UINT8(0, static_cast<uint8_t>(eth_clock_id_t::GPIO0_IN));
|
||||
TEST_ASSERT_EQUAL_UINT8(1, static_cast<uint8_t>(eth_clock_id_t::GPIO0_OUT));
|
||||
TEST_ASSERT_EQUAL_UINT8(2, static_cast<uint8_t>(eth_clock_id_t::GPIO16_OUT));
|
||||
TEST_ASSERT_EQUAL_UINT8(3, static_cast<uint8_t>(eth_clock_id_t::GPIO17_OUT));
|
||||
}
|
||||
static void test_translates_each_clock_mode() {
|
||||
eth_clock_mode_t mode;
|
||||
TEST_ASSERT_TRUE(ethClockMode(0, mode));
|
||||
TEST_ASSERT_EQUAL(ETH_CLOCK_GPIO0_IN, mode);
|
||||
TEST_ASSERT_TRUE(ethClockMode(1, mode));
|
||||
TEST_ASSERT_EQUAL(ETH_CLOCK_GPIO0_OUT, mode);
|
||||
TEST_ASSERT_TRUE(ethClockMode(2, mode));
|
||||
TEST_ASSERT_EQUAL(ETH_CLOCK_GPIO16_OUT, mode);
|
||||
TEST_ASSERT_TRUE(ethClockMode(3, mode));
|
||||
TEST_ASSERT_EQUAL(ETH_CLOCK_GPIO17_OUT, mode);
|
||||
}
|
||||
static void test_rejects_unknown_clock_ids() {
|
||||
eth_clock_mode_t mode;
|
||||
TEST_ASSERT_FALSE(ethClockMode(4, mode));
|
||||
TEST_ASSERT_FALSE(ethClockMode(255, mode));
|
||||
}
|
||||
static void test_clock_pins() {
|
||||
TEST_ASSERT_EQUAL_INT8(0, ethClockPin(0));
|
||||
TEST_ASSERT_EQUAL_INT8(0, ethClockPin(1));
|
||||
TEST_ASSERT_EQUAL_INT8(16, ethClockPin(2));
|
||||
TEST_ASSERT_EQUAL_INT8(17, ethClockPin(3));
|
||||
TEST_ASSERT_EQUAL_INT8(-1, ethClockPin(4));
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_ids_match_stored_values);
|
||||
|
|
@ -59,5 +92,9 @@ int main(int, char **) {
|
|||
RUN_TEST(test_lan8720_is_not_the_framework_zero);
|
||||
RUN_TEST(test_rejects_dm9051);
|
||||
RUN_TEST(test_rejects_unknown_ids);
|
||||
RUN_TEST(test_clock_ids_match_stored_values);
|
||||
RUN_TEST(test_translates_each_clock_mode);
|
||||
RUN_TEST(test_rejects_unknown_clock_ids);
|
||||
RUN_TEST(test_clock_pins);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
49
test/test_ethphy_p4/test_ethphy_p4.cpp
Normal file
49
test/test_ethphy_p4/test_ethphy_p4.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <unity.h>
|
||||
// As on the ESP32-P4, which has the RMII MAC too but takes the IDF's clock
|
||||
// enum.
|
||||
#define CONFIG_ETH_USE_ESP32_EMAC 1
|
||||
#define CONFIG_IDF_TARGET_ESP32P4 1
|
||||
#include "EthPhy.h"
|
||||
|
||||
void setUp() {}
|
||||
void tearDown() {}
|
||||
|
||||
static void test_builds_with_ethernet() {
|
||||
TEST_ASSERT_EQUAL(1, HAS_ETHERNET);
|
||||
}
|
||||
static void test_ip101_is_the_tlk110_id() {
|
||||
// Waveshare's ESP32-P4-ETH boards have an IP101, which Arduino drives as
|
||||
// the TLK110; the web UI's preset stores 1.
|
||||
eth_phy_type_t type = ETH_PHY_MAX;
|
||||
TEST_ASSERT_TRUE(ethPhyType(1, type));
|
||||
TEST_ASSERT_EQUAL(ETH_PHY_TLK110, type);
|
||||
}
|
||||
static void test_clock_in_is_ext_in() {
|
||||
// A cast would give the IDF's deprecated EMAC_CLK_DEFAULT.
|
||||
eth_clock_mode_t mode = EMAC_CLK_DEFAULT;
|
||||
TEST_ASSERT_TRUE(ethClockMode(static_cast<uint8_t>(eth_clock_id_t::GPIO0_IN), mode));
|
||||
TEST_ASSERT_EQUAL(EMAC_CLK_EXT_IN, mode);
|
||||
}
|
||||
static void test_rejects_clock_out() {
|
||||
// ETH.begin() keeps the clock on GPIO 50, which only takes a clock in.
|
||||
eth_clock_mode_t mode;
|
||||
TEST_ASSERT_FALSE(ethClockMode(1, mode));
|
||||
TEST_ASSERT_FALSE(ethClockMode(2, mode));
|
||||
TEST_ASSERT_FALSE(ethClockMode(3, mode));
|
||||
TEST_ASSERT_FALSE(ethClockMode(4, mode));
|
||||
}
|
||||
static void test_clock_pin_is_50() {
|
||||
TEST_ASSERT_EQUAL_INT8(50, ethClockPin(0));
|
||||
TEST_ASSERT_EQUAL_INT8(-1, ethClockPin(1));
|
||||
TEST_ASSERT_EQUAL_INT8(-1, ethClockPin(3));
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_builds_with_ethernet);
|
||||
RUN_TEST(test_ip101_is_the_tlk110_id);
|
||||
RUN_TEST(test_clock_in_is_ext_in);
|
||||
RUN_TEST(test_rejects_clock_out);
|
||||
RUN_TEST(test_clock_pin_is_50);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ static void test_whole_numbers_are_checked_against_what_the_setting_holds() {
|
|||
TEST_ASSERT_EQUAL_STRING("connMode must be 1-3", expand(R"({"se":"network","cn":0})"));
|
||||
TEST_ASSERT_EQUAL_STRING("port must be 1-65535", expand(R"({"se":"mqtt","po":65536})"));
|
||||
TEST_ASSERT_EQUAL_STRING("txPower must be -30-12", expand(R"({"se":"radio","tp":13})"));
|
||||
TEST_ASSERT_EQUAL_STRING("TXPin must be 0-48", expand(R"({"se":"radio","tx":-1})"));
|
||||
TEST_ASSERT_EQUAL_STRING("TXPin must be 0-54", expand(R"({"se":"radio","tx":-1})"));
|
||||
TEST_ASSERT_EQUAL_STRING("securityType must be 0-2", expand(R"({"se":"security","sy":3})"));
|
||||
TEST_ASSERT_EQUAL_STRING("port must be 1-65535", expand(R"({"se":"mqtt","po":18446744073709551615})"));
|
||||
// A pin of -1 is none.
|
||||
|
|
|
|||
|
|
@ -284,6 +284,10 @@ static void test_releaseAssetName_matches_the_release_workflow() {
|
|||
TEST_ASSERT_EQUAL_STRING("SomfyController.ino.esp32c5.bin", buf);
|
||||
releaseAssetName(buf, sizeof(buf), "littlefs", "c6");
|
||||
TEST_ASSERT_EQUAL_STRING("SomfyController.littlefs.esp32c6.bin", buf);
|
||||
releaseAssetName(buf, sizeof(buf), "ino", "p4");
|
||||
TEST_ASSERT_EQUAL_STRING("SomfyController.ino.esp32p4.bin", buf);
|
||||
releaseAssetName(buf, sizeof(buf), "littlefs", "p4es");
|
||||
TEST_ASSERT_EQUAL_STRING("SomfyController.littlefs.esp32p4es.bin", buf);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue