mirror of
https://github.com/arendst/Tasmota.git
synced 2025-04-24 23:07:17 +00:00
Initial wifi range extender (#12784)
This commit is contained in:
parent
b27b30cc8d
commit
c3a1ba8de2
@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
||||
## [9.5.0.4]
|
||||
### Added
|
||||
- Support for second DNS server
|
||||
- Optional IP filter to command ``TCPStart`` (#12806)
|
||||
|
||||
### Changed
|
||||
- ESP8266Audio library from v1.5.0 to v1.9.2
|
||||
|
@ -113,6 +113,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
|
||||
- Support for Technoline WS2300-15 Anemometer [#12573](https://github.com/arendst/Tasmota/issues/12573)
|
||||
- Support for Telaire T6700 Series CO2 sensor by Alexander Savchenko [#12618](https://github.com/arendst/Tasmota/issues/12618)
|
||||
- Support for CAN bus and Freedom Won Battery Management System by Marius Bezuidenhout [#12651](https://github.com/arendst/Tasmota/issues/12651)
|
||||
- Optional IP filter to command ``TCPStart`` [#12806](https://github.com/arendst/Tasmota/issues/12806)
|
||||
|
||||
### Changed
|
||||
- ESP32 core library from v1.0.6 to v1.0.7.3
|
||||
|
352
tasmota/xdrv_58_range_extender.ino
Normal file
352
tasmota/xdrv_58_range_extender.ino
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
xdrv_58_range_extender.ino - WiFi Range Extender for Tasmota
|
||||
|
||||
Copyright (C) 2021 sillyfrog and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER
|
||||
/*********************************************************************************************
|
||||
To use this, add the following to your user_config_override.h
|
||||
#define USE_WIFI_RANGE_EXTENDER
|
||||
|
||||
Additionally PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH must be set in your build options.
|
||||
For example, in your platfromio_tasmota_cenv.ini, you will need an entry such as:
|
||||
[env:tasmota-rangeextender]
|
||||
build_flags = ${common.build_flags}
|
||||
-D FIRMWARE_RANGE_EXTENDER
|
||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
|
||||
If you want to support NAPT (removing the need for routes on a core router):
|
||||
#define USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
|
||||
An example full static configuration:
|
||||
#define USE_WIFI_RANGE_EXTENDER
|
||||
#define USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
#define WIFI_RGX_SSID "rangeextender"
|
||||
#define WIFI_RGX_PASSWORD "securepassword"
|
||||
#define WIFI_RGX_IP_ADDRESS "10.99.1.1"
|
||||
#define WIFI_RGX_SUBNETMASK "255.255.255.0"
|
||||
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_58 58
|
||||
|
||||
// Memory usage at 512: Heap from 30136 to 17632: 12504
|
||||
// Memory usage at 128: Heap from 30136 to 26848: 3288
|
||||
#define NAPT 128 // IP_NAPT_MAX: 512
|
||||
#define NAPT_PORT 10 // IP_PORTMAP_MAX: 32
|
||||
|
||||
#warning **** USE_WIFI_RANGE_EXTENDER is enabled ****
|
||||
|
||||
#ifdef ESP8266
|
||||
#ifdef LWIP_FEATURES
|
||||
// All good
|
||||
#else
|
||||
#error LWIP_FEATURES required, add "-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH" to build_flags
|
||||
#endif // LWIP_FEATURES
|
||||
#endif // ESP8266
|
||||
|
||||
#ifdef ESP32
|
||||
#ifdef CONFIG_LWIP_IP_FORWARD
|
||||
// All good
|
||||
#else
|
||||
#error CONFIG_LWIP_IP_FORWARD not set, arduino-esp32 v2 or later required with CONFIG_LWIP_IP_FORWARD support
|
||||
#endif // CONFIG_LWIP_IP_FORWARD
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
#ifdef CONFIG_LWIP_IPV4_NAPT
|
||||
// All good
|
||||
#else
|
||||
#error CONFIG_LWIP_IPV4_NAPT not set, arduino-esp32 v2 or later required with CONFIG_LWIP_IPV4_NAPT support
|
||||
#endif // IP_NAPT
|
||||
#endif // CONFIG_LWIP_IPV4_NAPT
|
||||
#endif // ESP32
|
||||
|
||||
const char kDrvRgxCommands[] PROGMEM = "Rgx|" // Prefix
|
||||
"State"
|
||||
"|"
|
||||
D_CMND_SSID
|
||||
"|"
|
||||
D_CMND_PASSWORD
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
"|"
|
||||
"NAPT"
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
"|"
|
||||
"Address"
|
||||
"|"
|
||||
"Subnet";
|
||||
|
||||
void (*const DrvRgxCommand[])(void) PROGMEM = {
|
||||
&CmndRgxState,
|
||||
&CmndRgxSSID,
|
||||
&CmndRgxPassword,
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
&CmndRgxNAPT,
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
&CmndRgxAddresses,
|
||||
&CmndRgxAddresses,
|
||||
};
|
||||
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
#ifdef ESP8266
|
||||
#include <lwip/napt.h>
|
||||
#endif // ESP8266
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <lwip/dns.h>
|
||||
#ifdef ESP8266
|
||||
#include <dhcpserver.h>
|
||||
#endif // ESP8266
|
||||
#ifdef ESP32
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include <dhcpserver/dhcpserver.h>
|
||||
#endif // ESP32
|
||||
|
||||
#define RGX_NOT_CONFIGURED 0
|
||||
#define RGX_FORCE_CONFIGURE 1
|
||||
#define RGX_CONFIGURED 2
|
||||
#define RGX_CONFIG_INCOMPLETE 3
|
||||
#define RGX_SETUP_NAPT 4
|
||||
|
||||
typedef struct {
|
||||
uint8_t status = RGX_NOT_CONFIGURED;
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
bool napt_enabled = false;
|
||||
#ifdef ESP8266
|
||||
bool napt_inited = false;
|
||||
#endif // ESP8266
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
} TRgxSettings;
|
||||
|
||||
TRgxSettings RgxSettings;
|
||||
|
||||
// Check the current configuration is complete, updating RgxSettings.status
|
||||
void RgxCheckConfig(void)
|
||||
{
|
||||
if (
|
||||
strlen(SettingsText(SET_RGX_SSID)) > 0 &&
|
||||
strlen(SettingsText(SET_RGX_PASSWORD)) >= 8 &&
|
||||
Settings->ipv4_rgx_address &&
|
||||
Settings->ipv4_rgx_subnetmask)
|
||||
{
|
||||
if (RgxSettings.status != RGX_FORCE_CONFIGURE)
|
||||
{
|
||||
RgxSettings.status = RGX_NOT_CONFIGURED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RgxSettings.status = RGX_CONFIG_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
void CmndRgxState(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
||||
if (Settings->sbflag1.range_extender != XdrvMailbox.payload) {
|
||||
Settings->sbflag1.range_extender = XdrvMailbox.payload;
|
||||
if (0 == XdrvMailbox.payload) { // Turn off
|
||||
TasmotaGlobal.restart_flag = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseCmndStateText(Settings->sbflag1.range_extender);
|
||||
}
|
||||
|
||||
void CmndRgxAddresses(void) {
|
||||
char network_address[22];
|
||||
ext_snprintf_P(network_address, sizeof(network_address), PSTR(" (%_I)"), (uint32_t)NetworkAddress());
|
||||
uint32_t ipv4_address;
|
||||
if (ParseIPv4(&ipv4_address, XdrvMailbox.data)) {
|
||||
if (XdrvMailbox.command[3] == 'S') { // Subnet
|
||||
Settings->ipv4_rgx_subnetmask = ipv4_address;
|
||||
} else {
|
||||
Settings->ipv4_rgx_address = ipv4_address;
|
||||
}
|
||||
RgxSettings.status = RGX_FORCE_CONFIGURE;
|
||||
}
|
||||
ResponseRgxConfig();
|
||||
}
|
||||
|
||||
void CmndRgxSSID(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_RGX_SSID, (SC_CLEAR == Shortcut()) ? "" : XdrvMailbox.data);
|
||||
}
|
||||
RgxSettings.status = RGX_FORCE_CONFIGURE;
|
||||
ResponseRgxConfig();
|
||||
}
|
||||
|
||||
void CmndRgxPassword(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_RGX_PASSWORD, (SC_CLEAR == Shortcut()) ? "" : XdrvMailbox.data);
|
||||
}
|
||||
RgxSettings.status = RGX_FORCE_CONFIGURE;
|
||||
ResponseRgxConfig();
|
||||
}
|
||||
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
void CmndRgxNAPT(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
||||
if (Settings->sbflag1.range_extender_napt != XdrvMailbox.payload) {
|
||||
Settings->sbflag1.range_extender_napt = XdrvMailbox.payload;
|
||||
RgxSettings.status = RGX_FORCE_CONFIGURE;
|
||||
}
|
||||
}
|
||||
ResponseCmndStateText(Settings->sbflag1.range_extender_napt);
|
||||
};
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
|
||||
void ResponseRgxConfig(void)
|
||||
{
|
||||
RgxCheckConfig();
|
||||
Response_P(PSTR("{\"Rgx\":{\"Valid\":\"%s\",\"" D_CMND_SSID "\":\"%s\",\"" D_CMND_PASSWORD "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\"}"),
|
||||
(RgxSettings.status == RGX_CONFIG_INCOMPLETE) ? "false" : "true",
|
||||
EscapeJSONString(SettingsText(SET_RGX_SSID)).c_str(),
|
||||
EscapeJSONString(SettingsText(SET_RGX_PASSWORD)).c_str(),
|
||||
Settings->ipv4_rgx_address,
|
||||
Settings->ipv4_rgx_subnetmask);
|
||||
}
|
||||
|
||||
void rngxSetup()
|
||||
{
|
||||
// Check we have a complete config first
|
||||
RgxCheckConfig();
|
||||
if (RgxSettings.status == RGX_CONFIG_INCOMPLETE)
|
||||
{
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("RGX: Range Extender config incomplete"));
|
||||
return;
|
||||
}
|
||||
#ifdef ESP8266
|
||||
dhcps_set_dns(0, WiFi.dnsIP(0));
|
||||
dhcps_set_dns(1, WiFi.dnsIP(1));
|
||||
#endif // ESP8266
|
||||
#ifdef ESP32
|
||||
esp_err_t err;
|
||||
tcpip_adapter_dns_info_t ip_dns;
|
||||
|
||||
err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP);
|
||||
err = tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA, ESP_NETIF_DNS_MAIN, &ip_dns);
|
||||
err = tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_AP, ESP_NETIF_DNS_MAIN, &ip_dns);
|
||||
dhcps_offer_t opt_val = OFFER_DNS; // supply a dns server via dhcps
|
||||
tcpip_adapter_dhcps_option(ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &opt_val, 1);
|
||||
err = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP);
|
||||
#endif // ESP32
|
||||
// WiFi.softAPConfig(EXTENDER_LOCAL_IP, EXTENDER_GATEWAY_IP, EXTENDER_SUBNET);
|
||||
WiFi.softAPConfig(Settings->ipv4_rgx_address, Settings->ipv4_rgx_address, Settings->ipv4_rgx_subnetmask);
|
||||
WiFi.softAP(SettingsText(SET_RGX_SSID), SettingsText(SET_RGX_PASSWORD));
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("RGX: WiFi Extender AP Enabled"));
|
||||
RgxSettings.status = RGX_SETUP_NAPT;
|
||||
}
|
||||
|
||||
void rngxSetupNAPT(void) {
|
||||
// A short delay is required for enabling NAPT to work on the ESP32
|
||||
#ifdef USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
if (Settings->sbflag1.range_extender_napt && !RgxSettings.napt_enabled) {
|
||||
#ifdef ESP8266
|
||||
if (!RgxSettings.napt_inited) {
|
||||
err_t ret = ip_napt_init(NAPT, NAPT_PORT);
|
||||
if (ret == ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("RGX: NAPT initialization complete"));
|
||||
RgxSettings.napt_inited = true;
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("RGX: NAPT initialization failed! (%d)"), ret);
|
||||
}
|
||||
}
|
||||
if (RgxSettings.napt_inited) {
|
||||
err_t ret = ip_napt_enable_no(SOFTAP_IF, 1);
|
||||
if (ret == ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("RGX: NAPT Enabled"));
|
||||
RgxSettings.napt_enabled = true;
|
||||
}
|
||||
}
|
||||
#endif // ESP8266
|
||||
#ifdef ESP32
|
||||
ip_napt_enable(WiFi.softAPIP(), 1);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("RGX: NAPT Enabled"));
|
||||
RgxSettings.napt_enabled = true;
|
||||
#endif // ESP32
|
||||
}
|
||||
else if (!Settings->sbflag1.range_extender_napt && RgxSettings.napt_enabled) {
|
||||
#ifdef ESP8266
|
||||
err_t ret = ip_napt_enable_no(SOFTAP_IF, 0);
|
||||
if (ret == ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, "RGX: NAPT Disabled");
|
||||
RgxSettings.napt_enabled = false;
|
||||
}
|
||||
#endif // ESP8266
|
||||
#ifdef ESP32
|
||||
ip_napt_enable(WiFi.softAPIP(), 0);
|
||||
AddLog(LOG_LEVEL_INFO, "RGX: NAPT Disabled, reboot maybe required");
|
||||
#endif // ESP32
|
||||
}
|
||||
#endif // USE_WIFI_RANGE_EXTENDER_NAPT
|
||||
RgxSettings.status = RGX_CONFIGURED;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv58(uint8_t function) {
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_COMMAND == function) {
|
||||
result = DecodeCommand(kDrvRgxCommands, DrvRgxCommand);
|
||||
}
|
||||
else if (Settings->sbflag1.range_extender && !TasmotaGlobal.restart_flag) {
|
||||
switch (function)
|
||||
{
|
||||
case FUNC_PRE_INIT:
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
// AddLog(LOG_LEVEL_INFO, PSTR("RGX: XXX DEBUG INFO: Wifi.status: %d, WiFi.getMode(): %d, RgxSettings.status: %d"), Wifi.status, WiFi.getMode(), RgxSettings.status);
|
||||
if (RgxSettings.status == RGX_NOT_CONFIGURED && Wifi.status == WL_CONNECTED)
|
||||
{
|
||||
// Setup only if WiFi in STA only mode
|
||||
if (WiFi.getMode() == WIFI_STA)
|
||||
{
|
||||
// Connecting for the first time, setup WiFi
|
||||
rngxSetup();
|
||||
}
|
||||
else
|
||||
{
|
||||
RgxSettings.status = RGX_CONFIGURED;
|
||||
}
|
||||
}
|
||||
else if (RgxSettings.status == RGX_FORCE_CONFIGURE && Wifi.status == WL_CONNECTED)
|
||||
{
|
||||
rngxSetup();
|
||||
}
|
||||
else if (RgxSettings.status == RGX_SETUP_NAPT)
|
||||
{
|
||||
rngxSetupNAPT();
|
||||
}
|
||||
else if (RgxSettings.status == RGX_CONFIGURED)
|
||||
{
|
||||
if (Wifi.status != WL_CONNECTED)
|
||||
{
|
||||
// No longer connected, need to setup again
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("RGX: No longer connected, prepare to reconnect WiFi AP..."));
|
||||
RgxSettings.status = RGX_NOT_CONFIGURED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_WIFI_RANGE_EXTENDER
|
Loading…
x
Reference in New Issue
Block a user