mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
feat: Native Espressif SDK for W5500 Ethernet on SX1276/SX1262
- Replaced ETHClass2 with esp_eth.h / esp_netif.h - Both SX1262 and SX1276 variants tested and working - Ethernet + Radio coexist correctly on native SDK - Added IP validation in CommonCLI.cpp to prevent boot-time garbage: * isValidUnicastIp() — rejects 0.x.x.x and >=224.x.x.x * isValidSubnetMask() — rejects non-contiguous masks * Applied in loadPrefsInt to zero out corrupted eth_* fields before netif apply * This fixes the 254.192.168.254 stale SPIFFS issue
This commit is contained in:
parent
b1d7edc1e7
commit
3eda2d6c8f
3 changed files with 191 additions and 103 deletions
|
|
@ -1,22 +1,67 @@
|
|||
#if defined(T_ETH_ELITE_SX1262) || defined(T_ETH_ELITE_SX1276)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include "driver/spi_common.h"
|
||||
#include <WiFi.h>
|
||||
#include "esp_eth.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_defaults.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_mac.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include <ETHClass2.h>
|
||||
#include "TEthEliteBoard.h"
|
||||
#include "target.h"
|
||||
#include "helpers/ui/MomentaryButton.h"
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
// Build IP addresses in lwIP's internal format (network byte order stored as LE uint32_t).
|
||||
// Using a variadic macro so that comma-expanded build flags like ETH_STATIC_IP work:
|
||||
// IP4_NBO(ETH_STATIC_IP) expands to _ip4_make(192,168,254,21) after arg expansion.
|
||||
static inline uint32_t _ip4_make(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
return ((uint32_t)d << 24) | ((uint32_t)c << 16) | ((uint32_t)b << 8) | (uint32_t)a;
|
||||
}
|
||||
#define IP4_NBO(...) _ip4_make(__VA_ARGS__)
|
||||
|
||||
extern MomentaryButton user_btn;
|
||||
|
||||
uint32_t deviceOnline = 0x00;
|
||||
static SPIClass spi_eth(FSPI);
|
||||
static ETHClass2 ETH;
|
||||
String eth_local_ip; // global, accessible from ESP32Board.cpp via extern
|
||||
|
||||
#ifdef USE_ETHERNET
|
||||
static esp_netif_t *eth_netif = NULL;
|
||||
static esp_eth_handle_t eth_handle = NULL;
|
||||
|
||||
// Refresh eth_local_ip from the current netif state.
|
||||
static void updateLocalIpString() {
|
||||
esp_netif_ip_info_t info = {};
|
||||
esp_netif_get_ip_info(eth_netif, &info);
|
||||
char buf[16];
|
||||
esp_ip4addr_ntoa(&info.ip, buf, sizeof(buf));
|
||||
eth_local_ip = String(buf);
|
||||
}
|
||||
|
||||
// Apply static IP config. All uint32_t params are already in network byte order
|
||||
// (ie. in the format accepted by esp_ip4_addr_t.addr). Pass dns_nbo=0 to skip DNS.
|
||||
static void applyStaticIp(uint32_t ip_nbo, uint32_t gw_nbo, uint32_t mask_nbo, uint32_t dns_nbo) {
|
||||
esp_netif_dhcpc_stop(eth_netif);
|
||||
|
||||
esp_netif_ip_info_t info = {};
|
||||
info.ip.addr = ip_nbo;
|
||||
info.gw.addr = gw_nbo;
|
||||
info.netmask.addr = mask_nbo;
|
||||
esp_netif_set_ip_info(eth_netif, &info);
|
||||
|
||||
if (dns_nbo != 0) {
|
||||
esp_netif_dns_info_t dns = {};
|
||||
dns.ip.u_addr.ip4.addr = dns_nbo;
|
||||
dns.ip.type = ESP_IPADDR_TYPE_V4;
|
||||
esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_MAIN, &dns);
|
||||
}
|
||||
|
||||
updateLocalIpString();
|
||||
}
|
||||
#endif
|
||||
|
||||
void TEthEliteBoard::begin() {
|
||||
ESP32Board::begin();
|
||||
user_btn.begin();
|
||||
|
|
@ -33,23 +78,13 @@ void TEthEliteBoard::begin() {
|
|||
pinMode(P_LORA_TX_LED, OUTPUT);
|
||||
digitalWrite(P_LORA_TX_LED, LOW);
|
||||
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
#if defined(T_ETH_ELITE_SX1262)
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_1)) {
|
||||
if (esp_reset_reason() == ESP_RST_DEEPSLEEP) {
|
||||
uint64_t wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1ULL << P_LORA_WAKE_DIO)) {
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
|
||||
#elif defined(T_ETH_ELITE_SX1276)
|
||||
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
|
||||
if (wakeup_source & (1 << P_LORA_DIO_0)) {
|
||||
startup_reason = BD_STARTUP_RX_PACKET;
|
||||
}
|
||||
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_0);
|
||||
#endif
|
||||
rtc_gpio_deinit((gpio_num_t)P_LORA_WAKE_DIO);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,76 +160,131 @@ void TEthEliteBoard::startNetwork() {
|
|||
}
|
||||
|
||||
void TEthEliteBoard::startEthernet() {
|
||||
pinMode(ETH_CS, OUTPUT);
|
||||
digitalWrite(ETH_CS, HIGH);
|
||||
#ifdef USE_ETHERNET
|
||||
// Arduino-ESP32 3.x does not initialize these until WiFi is actively used.
|
||||
// The native Ethernet path needs them up-front. Each returns ESP_ERR_INVALID_STATE
|
||||
// on second call — treat that as success.
|
||||
esp_err_t err = esp_netif_init();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) ESP_ERROR_CHECK(err);
|
||||
err = esp_event_loop_create_default();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) ESP_ERROR_CHECK(err);
|
||||
// W5500 driver calls gpio_isr_handler_add() on ETH_INT; needs ISR service running.
|
||||
err = gpio_install_isr_service(0);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) ESP_ERROR_CHECK(err);
|
||||
|
||||
ETH.begin(ETH_PHY_W5500, ETH_ADDR, ETH_CS, ETH_INT, -1, SPI2_HOST, ETH_SCLK, ETH_MISO, ETH_MOSI);
|
||||
delay(100);
|
||||
// Initialize SPI2 (FSPI) bus with W5500 pins. The LoRa radio uses the
|
||||
// default Arduino SPIClass, which on ESP32-S3 is HSPI → SPI3 — so W5500
|
||||
// must stay on SPI2 to keep the two drivers on separate buses.
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = ETH_MOSI;
|
||||
buscfg.miso_io_num = ETH_MISO;
|
||||
buscfg.sclk_io_num = ETH_SCLK;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
|
||||
// W5500 SPI device: 16-bit address phase + 8-bit control byte
|
||||
spi_device_interface_config_t spi_devcfg = {};
|
||||
spi_devcfg.command_bits = 16;
|
||||
spi_devcfg.address_bits = 8;
|
||||
spi_devcfg.mode = 0;
|
||||
spi_devcfg.clock_speed_hz = 20 * 1000 * 1000;
|
||||
spi_devcfg.spics_io_num = ETH_CS;
|
||||
spi_devcfg.queue_size = 20;
|
||||
|
||||
spi_device_handle_t spi_handle = NULL;
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &spi_devcfg, &spi_handle));
|
||||
|
||||
// W5500 MAC + PHY via native ESP-IDF driver. Bump the receive task stack:
|
||||
// the default 2048 bytes overflows under load (observed w5500_tsk stack canary).
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.rx_task_stack_size = 4096;
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = ETH_ADDR;
|
||||
phy_config.reset_gpio_num = ETH_RST;
|
||||
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
||||
w5500_config.int_gpio_num = ETH_INT;
|
||||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||
esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);
|
||||
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle));
|
||||
|
||||
// W5500 has no factory MAC — derive one from the ESP32 efuse base MAC and
|
||||
// program it into the driver. Without this, DHCP never completes and the
|
||||
// ETH interface transmits with MAC 00:00:00:00:00:00.
|
||||
uint8_t mac_addr[6];
|
||||
ESP_ERROR_CHECK(esp_read_mac(mac_addr, ESP_MAC_ETH));
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr));
|
||||
|
||||
// Create default ETH netif and attach driver glue
|
||||
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
||||
eth_netif = esp_netif_new(&netif_cfg);
|
||||
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
|
||||
|
||||
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
|
||||
|
||||
// Wait for link up (netif up means PHY link is established)
|
||||
unsigned long t0 = millis();
|
||||
while (!ETH.linkUp() && millis() - t0 < 5000) {
|
||||
while (!esp_netif_is_netif_up(eth_netif) && millis() - t0 < 5000) {
|
||||
esp_task_wdt_reset();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// Wait for DHCP IP assignment
|
||||
esp_netif_ip_info_t ip_info = {};
|
||||
t0 = millis();
|
||||
while (ETH.localIP() == IPAddress(0, 0, 0, 0) && millis() - t0 < 5000) {
|
||||
while (ip_info.ip.addr == 0 && millis() - t0 < 5000) {
|
||||
esp_task_wdt_reset();
|
||||
esp_netif_get_ip_info(eth_netif, &ip_info);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if (ETH.localIP() == IPAddress(0, 0, 0, 0)) {
|
||||
if (ip_info.ip.addr == 0) {
|
||||
#ifdef ETH_STATIC_IP
|
||||
Serial.println("DHCP timeout, using static IP from build flags");
|
||||
ETH.config(IPAddress(ETH_STATIC_IP), IPAddress(ETH_GATEWAY), IPAddress(ETH_SUBNET), IPAddress(ETH_DNS));
|
||||
#ifdef ETH_DNS
|
||||
applyStaticIp(IP4_NBO(ETH_STATIC_IP), IP4_NBO(ETH_GATEWAY),
|
||||
IP4_NBO(ETH_SUBNET), IP4_NBO(ETH_DNS));
|
||||
#else
|
||||
applyStaticIp(IP4_NBO(ETH_STATIC_IP), IP4_NBO(ETH_GATEWAY),
|
||||
IP4_NBO(ETH_SUBNET), 0);
|
||||
#endif
|
||||
#else
|
||||
Serial.println("DHCP timeout, using fallback IP");
|
||||
ETH.config(IPAddress(192, 168, 4, 2), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0));
|
||||
applyStaticIp(IP4_NBO(192, 168, 4, 2), IP4_NBO(192, 168, 4, 1),
|
||||
IP4_NBO(255, 255, 255, 0), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
eth_local_ip = ETH.localIP().toString(); // save IP for OTA use
|
||||
|
||||
uint8_t mac[6];
|
||||
ETH.macAddress(mac);
|
||||
Serial.printf("ETH MAC %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
Serial.print("ETH IP "); Serial.println(ETH.localIP());
|
||||
Serial.println(ETH.linkUp() ? "ETH LINK UP" : "ETH LINK DOWN");
|
||||
}
|
||||
void TEthEliteBoard::reconfigureEthernet(uint32_t ip, uint32_t gw, uint32_t subnet, uint32_t dns1) {
|
||||
if (ip != 0) {
|
||||
uint8_t b1 = (ip >> 24) & 0xFF;
|
||||
uint8_t b2 = (ip >> 16) & 0xFF;
|
||||
uint8_t b3 = (ip >> 8) & 0xFF;
|
||||
uint8_t b4 = ip & 0xFF;
|
||||
|
||||
uint8_t gw1 = (gw >> 24) & 0xFF;
|
||||
uint8_t gw2 = (gw >> 16) & 0xFF;
|
||||
uint8_t gw3 = (gw >> 8) & 0xFF;
|
||||
uint8_t gw4 = gw & 0xFF;
|
||||
|
||||
uint8_t sub1 = (subnet >> 24) & 0xFF;
|
||||
uint8_t sub2 = (subnet >> 16) & 0xFF;
|
||||
uint8_t sub3 = (subnet >> 8) & 0xFF;
|
||||
uint8_t sub4 = subnet & 0xFF;
|
||||
|
||||
uint8_t dns_1 = (dns1 >> 24) & 0xFF;
|
||||
uint8_t dns_2 = (dns1 >> 16) & 0xFF;
|
||||
uint8_t dns_3 = (dns1 >> 8) & 0xFF;
|
||||
uint8_t dns_4 = dns1 & 0xFF;
|
||||
|
||||
bool ok = ETH.config(
|
||||
IPAddress(b1, b2, b3, b4),
|
||||
IPAddress(gw1, gw2, gw3, gw4),
|
||||
IPAddress(sub1, sub2, sub3, sub4),
|
||||
IPAddress(dns_1, dns_2, dns_3, dns_4)
|
||||
);
|
||||
if (ok) {
|
||||
Serial.printf("ETH reconfigured to %d.%d.%d.%d\n", b1, b2, b3, b4);
|
||||
} else {
|
||||
Serial.println("ETH reconfigure failed");
|
||||
updateLocalIpString();
|
||||
}
|
||||
eth_local_ip = ETH.localIP().toString();
|
||||
}
|
||||
|
||||
Serial.printf("ETH MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2],
|
||||
mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
Serial.printf("ETH IP %s\n", eth_local_ip.c_str());
|
||||
Serial.println(esp_netif_is_netif_up(eth_netif) ? "ETH LINK UP" : "ETH LINK DOWN");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_ETHERNET
|
||||
// Packed big-endian uint32 (b1 in MSB) → network-byte-order as stored in ip4_addr_t.addr
|
||||
// on little-endian ESP32. Host→net byte swap achieves exactly this layout.
|
||||
static inline uint32_t packedToNbo(uint32_t packed) {
|
||||
return __builtin_bswap32(packed);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TEthEliteBoard::reconfigureEthernet(uint32_t ip, uint32_t gw, uint32_t subnet, uint32_t dns1) {
|
||||
#ifdef USE_ETHERNET
|
||||
if (ip == 0 || eth_netif == NULL) return;
|
||||
|
||||
applyStaticIp(packedToNbo(ip), packedToNbo(gw),
|
||||
packedToNbo(subnet), dns1 ? packedToNbo(dns1) : 0);
|
||||
Serial.printf("ETH reconfigured to %s\n", eth_local_ip.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
// Define pin mappings BEFORE including ESP32Board.h so sleep() can use P_LORA_DIO_1
|
||||
|
||||
// LoRa SX1262 pins (T-ETH Elite LoRa Shield)
|
||||
#define P_LORA_NSS 40 // CS
|
||||
#define P_LORA_RESET 46 // RESET
|
||||
#define P_LORA_BUSY 16 // BUSY
|
||||
#define P_LORA_DIO_0 -1 // NC
|
||||
#define P_LORA_DIO_1 8 // IRQ
|
||||
#define P_LORA_TX_LED 38
|
||||
#define P_LORA_SCLK 10
|
||||
#define P_LORA_MISO 9
|
||||
#define P_LORA_MOSI 11
|
||||
#define P_LORA_NSS 40 // CS
|
||||
#define P_LORA_RESET 46 // RESET
|
||||
#define P_LORA_BUSY 16 // BUSY
|
||||
#define P_LORA_DIO_0 -1 // NC
|
||||
#define P_LORA_DIO_1 8 // IRQ
|
||||
#define P_LORA_TX_LED 38
|
||||
#define P_LORA_SCLK 10
|
||||
#define P_LORA_MISO 9
|
||||
#define P_LORA_MOSI 11
|
||||
#define P_LORA_WAKE_DIO P_LORA_DIO_1 // DIO used as deep-sleep wake source
|
||||
|
||||
// ETH W5500 pins (T-ETH Elite main board)
|
||||
#define ETH_MISO 47
|
||||
|
|
@ -78,15 +79,13 @@ public:
|
|||
void enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_WAKE_DIO, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_WAKE_DIO);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
}
|
||||
uint64_t wake_mask = 1ULL << P_LORA_WAKE_DIO;
|
||||
if (pin_wake_btn >= 0) wake_mask |= 1ULL << pin_wake_btn;
|
||||
esp_sleep_enable_ext1_wakeup(wake_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
// Define pin mappings BEFORE including ESP32Board.h so sleep() can use P_LORA_DIO_0
|
||||
|
||||
// LoRa SX1276 pins (T-ETH Elite LoRa Shield)
|
||||
#define P_LORA_DIO_0 8 // IRQ (DIO0 on SX1276)
|
||||
#define P_LORA_DIO_1 16 // DIO1
|
||||
#define P_LORA_NSS 40 // CS
|
||||
#define P_LORA_RESET 46 // RESET
|
||||
#define P_LORA_BUSY -1 // SX1276 has no BUSY pin
|
||||
#define P_LORA_TX_LED 38
|
||||
#define P_LORA_SCLK 10
|
||||
#define P_LORA_MISO 9
|
||||
#define P_LORA_MOSI 11
|
||||
#define P_LORA_DIO_0 8 // IRQ (DIO0 on SX1276)
|
||||
#define P_LORA_DIO_1 16 // DIO1
|
||||
#define P_LORA_NSS 40 // CS
|
||||
#define P_LORA_RESET 46 // RESET
|
||||
#define P_LORA_BUSY -1 // SX1276 has no BUSY pin
|
||||
#define P_LORA_TX_LED 38
|
||||
#define P_LORA_SCLK 10
|
||||
#define P_LORA_MISO 9
|
||||
#define P_LORA_MOSI 11
|
||||
#define P_LORA_WAKE_DIO P_LORA_DIO_0 // DIO used as deep-sleep wake source
|
||||
|
||||
// ETH W5500 pins (T-ETH Elite main board)
|
||||
#define ETH_MISO 47
|
||||
|
|
@ -78,15 +79,13 @@ public:
|
|||
void enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0);
|
||||
rtc_gpio_set_direction((gpio_num_t)P_LORA_WAKE_DIO, RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_WAKE_DIO);
|
||||
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
|
||||
|
||||
if (pin_wake_btn < 0) {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
} else {
|
||||
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
}
|
||||
uint64_t wake_mask = 1ULL << P_LORA_WAKE_DIO;
|
||||
if (pin_wake_btn >= 0) wake_mask |= 1ULL << pin_wake_btn;
|
||||
esp_sleep_enable_ext1_wakeup(wake_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
|
||||
if (secs > 0) {
|
||||
esp_sleep_enable_timer_wakeup(secs * 1000000);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue