mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
feat: Add Ethernet runtime configuration CLI
- Add ip/gw/subnet/dns get/set commands with SPIFFS persistence - Implement reconfigureEthernet() method for runtime config - Fix TCP_CONSOLE_PORT conditional compilation - Update platformio.ini for consistency (USE_ETHERNET) - Document Ethernet settings and get bridge.type command - Fixes #2196 #2197 hardware and console dependencies
This commit is contained in:
parent
ea72dd0f97
commit
4542e6c86a
12 changed files with 295 additions and 16 deletions
|
|
@ -66,6 +66,7 @@ public:
|
|||
virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; }
|
||||
virtual uint8_t getShutdownReason() const { return 0; }
|
||||
virtual const char* getShutdownReasonString(uint8_t reason) { return "Not available"; }
|
||||
virtual void reconfigureEthernet(uint32_t ip, uint32_t gw, uint32_t subnet) { /* no op */ }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,6 +26,38 @@ static bool isValidName(const char *n) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Helper functions for IP address conversion
|
||||
static uint32_t ipStringToUint32(const char* ip_str) {
|
||||
uint32_t ip = 0;
|
||||
uint8_t parts[4] = {0, 0, 0, 0};
|
||||
sscanf(ip_str, "%hhu.%hhu.%hhu.%hhu", &parts[0], &parts[1], &parts[2], &parts[3]);
|
||||
ip = ((uint32_t)parts[0] << 24) | ((uint32_t)parts[1] << 16) | ((uint32_t)parts[2] << 8) | parts[3];
|
||||
return ip;
|
||||
}
|
||||
|
||||
static void uint32ToIPString(uint32_t ip, char* buffer, size_t size) {
|
||||
uint8_t b1 = (ip >> 24) & 0xFF;
|
||||
uint8_t b2 = (ip >> 16) & 0xFF;
|
||||
uint8_t b3 = (ip >> 8) & 0xFF;
|
||||
uint8_t b4 = ip & 0xFF;
|
||||
snprintf(buffer, size, "%d.%d.%d.%d", b1, b2, b3, b4);
|
||||
}
|
||||
|
||||
// Reject 0.x.x.x and multicast/reserved (>= 224.x.x.x). 0 is valid (means DHCP/clear).
|
||||
// Guards against stale bytes from older prefs-file layouts ending up at the netif.
|
||||
static bool isValidUnicastIp(uint32_t ip) {
|
||||
if (ip == 0) return true;
|
||||
uint8_t first = (ip >> 24) & 0xFF;
|
||||
return (first >= 1 && first <= 223);
|
||||
}
|
||||
|
||||
// Valid subnet mask = contiguous 1s followed by contiguous 0s.
|
||||
static bool isValidSubnetMask(uint32_t mask) {
|
||||
if (mask == 0) return true;
|
||||
uint32_t inv = ~mask;
|
||||
return (inv & (inv + 1)) == 0;
|
||||
}
|
||||
|
||||
void CommonCLI::loadPrefs(FILESYSTEM* fs) {
|
||||
if (fs->exists("/com_prefs")) {
|
||||
loadPrefsInt(fs, "/com_prefs"); // new filename
|
||||
|
|
@ -87,8 +119,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
file.read((uint8_t *)&_prefs->eth_ip, sizeof(_prefs->eth_ip)); // 291
|
||||
file.read((uint8_t *)&_prefs->eth_gateway, sizeof(_prefs->eth_gateway)); // 295
|
||||
file.read((uint8_t *)&_prefs->eth_subnet, sizeof(_prefs->eth_subnet)); // 299
|
||||
file.read((uint8_t *)&_prefs->eth_dns1, sizeof(_prefs->eth_dns1)); // 303
|
||||
file.read((uint8_t *)&_prefs->eth_dns2, sizeof(_prefs->eth_dns2)); // 307
|
||||
// 311
|
||||
|
||||
// sanitise bad pref values
|
||||
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||
|
|
@ -119,6 +156,15 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||
// sanitise settings
|
||||
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
|
||||
|
||||
// Sanitise eth_* fields: an older firmware version may have written different
|
||||
// data at these offsets. Drop anything that isn't a plausible unicast IP / mask
|
||||
// so we don't push garbage into esp_netif_set_ip_info() at boot.
|
||||
if (!isValidUnicastIp(_prefs->eth_ip)) _prefs->eth_ip = 0;
|
||||
if (!isValidUnicastIp(_prefs->eth_gateway)) _prefs->eth_gateway = 0;
|
||||
if (!isValidUnicastIp(_prefs->eth_dns1)) _prefs->eth_dns1 = 0;
|
||||
if (!isValidUnicastIp(_prefs->eth_dns2)) _prefs->eth_dns2 = 0;
|
||||
if (!isValidSubnetMask(_prefs->eth_subnet)) _prefs->eth_subnet = 0;
|
||||
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -178,9 +224,13 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
|||
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
|
||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
file.write((uint8_t *)&_prefs->eth_ip, sizeof(_prefs->eth_ip)); // 291
|
||||
file.write((uint8_t *)&_prefs->eth_gateway, sizeof(_prefs->eth_gateway)); // 295
|
||||
file.write((uint8_t *)&_prefs->eth_subnet, sizeof(_prefs->eth_subnet)); // 299
|
||||
file.write((uint8_t *)&_prefs->eth_dns1, sizeof(_prefs->eth_dns1)); // 303
|
||||
file.write((uint8_t *)&_prefs->eth_dns2, sizeof(_prefs->eth_dns2)); // 307
|
||||
// 311
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -725,6 +775,44 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
|||
_prefs->adc_multiplier = 0.0f;
|
||||
strcpy(reply, "Error: unsupported by this board");
|
||||
};
|
||||
#ifdef USE_ETHERNET
|
||||
} else if (memcmp(config, "ip ", 3) == 0) {
|
||||
uint32_t ip = ipStringToUint32(&config[3]);
|
||||
if (ip == UINT32_MAX || (ip != 0 && !isValidUnicastIp(ip))) {
|
||||
strcpy(reply, "Error: invalid IP");
|
||||
} else {
|
||||
_prefs->eth_ip = ip;
|
||||
savePrefs();
|
||||
strcpy(reply, ip == 0 ? "OK - reboot to apply (will use DHCP)" : "OK - reboot to apply");
|
||||
}
|
||||
} else if (memcmp(config, "subnet ", 7) == 0) {
|
||||
uint32_t subnet = ipStringToUint32(&config[7]);
|
||||
if (subnet == UINT32_MAX || !isValidSubnetMask(subnet)) {
|
||||
strcpy(reply, "Error: invalid subnet mask");
|
||||
} else {
|
||||
_prefs->eth_subnet = subnet;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - reboot to apply");
|
||||
}
|
||||
} else if (memcmp(config, "gw ", 3) == 0) {
|
||||
uint32_t gw = ipStringToUint32(&config[3]);
|
||||
if (gw == UINT32_MAX || (gw != 0 && !isValidUnicastIp(gw))) {
|
||||
strcpy(reply, "Error: invalid IP");
|
||||
} else {
|
||||
_prefs->eth_gateway = gw;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - reboot to apply");
|
||||
}
|
||||
} else if (memcmp(config, "dns ", 4) == 0) {
|
||||
uint32_t dns = ipStringToUint32(&config[4]);
|
||||
if (dns == UINT32_MAX || (dns != 0 && !isValidUnicastIp(dns))) {
|
||||
strcpy(reply, "Error: invalid IP");
|
||||
} else {
|
||||
_prefs->eth_dns1 = dns;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - reboot to apply");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
sprintf(reply, "unknown config: %s", config);
|
||||
}
|
||||
|
|
@ -840,6 +928,24 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
|||
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_channel);
|
||||
} else if (memcmp(config, "bridge.secret", 13) == 0) {
|
||||
sprintf(reply, "> %s", _prefs->bridge_secret);
|
||||
#endif
|
||||
#ifdef USE_ETHERNET
|
||||
} else if (memcmp(config, "ip", 2) == 0) {
|
||||
char ip_str[16];
|
||||
uint32ToIPString(_prefs->eth_ip, ip_str, sizeof(ip_str));
|
||||
sprintf(reply, "> %s", ip_str);
|
||||
} else if (memcmp(config, "subnet", 6) == 0) {
|
||||
char subnet_str[16];
|
||||
uint32ToIPString(_prefs->eth_subnet, subnet_str, sizeof(subnet_str));
|
||||
sprintf(reply, "> %s", subnet_str);
|
||||
} else if (memcmp(config, "gw", 2) == 0) {
|
||||
char gw_str[16];
|
||||
uint32ToIPString(_prefs->eth_gateway, gw_str, sizeof(gw_str));
|
||||
sprintf(reply, "> %s", gw_str);
|
||||
} else if (memcmp(config, "dns", 3) == 0) {
|
||||
char dns_str[16];
|
||||
uint32ToIPString(_prefs->eth_dns1, dns_str, sizeof(dns_str));
|
||||
sprintf(reply, "> %s", dns_str);
|
||||
#endif
|
||||
} else if (memcmp(config, "bootloader.ver", 14) == 0) {
|
||||
#ifdef NRF52_PLATFORM
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ struct NodePrefs { // persisted to file
|
|||
uint8_t rx_boosted_gain; // power settings
|
||||
uint8_t path_hash_mode; // which path mode to use when sending
|
||||
uint8_t loop_detect;
|
||||
// Ethernet settings
|
||||
uint32_t eth_ip;
|
||||
uint32_t eth_gateway;
|
||||
uint32_t eth_subnet;
|
||||
uint32_t eth_dns1;
|
||||
uint32_t eth_dns2;
|
||||
};
|
||||
|
||||
class CommonCLICallbacks {
|
||||
|
|
|
|||
|
|
@ -130,12 +130,15 @@ void TEthEliteBoard::startEthernet() {
|
|||
ETH.begin(ETH_PHY_W5500, ETH_ADDR, ETH_CS, ETH_INT, -1, SPI2_HOST, ETH_SCLK, ETH_MISO, ETH_MOSI);
|
||||
delay(100);
|
||||
|
||||
#ifdef ETH_STATIC_IP
|
||||
#ifndef USE_ETHERNET
|
||||
// Used only if USE_ETHERNET is not enabled
|
||||
#ifdef ETH_STATIC_IP
|
||||
IPAddress ip(ETH_STATIC_IP);
|
||||
IPAddress gw(ETH_GATEWAY);
|
||||
IPAddress mask(ETH_SUBNET);
|
||||
IPAddress dns(ETH_DNS);
|
||||
ETH.config(ip, gw, mask, dns);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
unsigned long t0 = millis();
|
||||
|
|
@ -161,5 +164,30 @@ void TEthEliteBoard::startEthernet() {
|
|||
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) {
|
||||
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;
|
||||
|
||||
ETH.config(
|
||||
IPAddress(b1, b2, b3, b4),
|
||||
IPAddress(gw1, gw2, gw3, gw4),
|
||||
IPAddress(sub1, sub2, sub3, sub4)
|
||||
);
|
||||
Serial.printf("ETH reconfigured to %d.%d.%d.%d\n", b1, b2, b3, b4);
|
||||
eth_local_ip = ETH.localIP().toString(); // Aggiorna IP locale per OTA
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,7 +73,8 @@ public:
|
|||
void startNetwork();
|
||||
void startEthernet();
|
||||
void startWifi();
|
||||
|
||||
void reconfigureEthernet(uint32_t ip, uint32_t gw, uint32_t subnet);
|
||||
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ public:
|
|||
void startNetwork();
|
||||
void startEthernet();
|
||||
void startWifi();
|
||||
void reconfigureEthernet(uint32_t ip, uint32_t gw, uint32_t subnet);
|
||||
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn) {
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue