├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── build_web_pages.bat ├── build_web_pages.sh ├── components ├── cmd_nvs │ ├── CMakeLists.txt │ ├── cmd_nvs.c │ ├── cmd_nvs.h │ └── component.mk ├── cmd_router │ ├── CMakeLists.txt │ ├── cmd_router.c │ ├── cmd_router.h │ ├── component.mk │ └── router_globals.h ├── cmd_system │ ├── CMakeLists.txt │ ├── cmd_system.c │ ├── cmd_system.h │ └── component.mk ├── console_handler │ ├── CMakeLists.txt │ ├── component.mk │ ├── console_handler.c │ ├── file_system.c │ └── include │ │ ├── console_handler.h │ │ └── file_system.h ├── hardware_handler │ ├── CMakeLists.txt │ ├── button_handler.c │ ├── component.mk │ ├── hardware_handler.c │ ├── include │ │ ├── button_handler.h │ │ ├── hardware_handler.h │ │ └── led_handler.h │ └── led_handler.c ├── ota_handler │ ├── CMakeLists.txt │ ├── README.md │ ├── component.mk │ ├── include │ │ └── ota_handler.h │ └── ota_handler.c ├── router_handler │ ├── CMakeLists.txt │ ├── component.mk │ ├── include │ │ └── router_handler.h │ └── router_handler.c ├── utils │ ├── CMakeLists.txt │ ├── component.mk │ ├── include │ │ ├── initialization.h │ │ ├── mac_filter.h │ │ ├── mac_generator.h │ │ ├── nvm.h │ │ └── utils.h │ ├── initialization.c │ ├── mac_filter.c │ ├── mac_generator.c │ ├── nvm.c │ └── utils.c ├── web_server │ ├── CMakeLists.txt │ ├── auth_handler.c │ ├── component.mk │ ├── get_data_handler.c │ ├── include │ │ ├── auth_handler.h │ │ ├── get_data_handler.h │ │ ├── request_handler.h │ │ ├── storage_handler.h │ │ └── web_server.h │ ├── request_handler.c │ ├── response_handler.c │ ├── storage_handler.c │ ├── web_server.c │ └── www │ │ ├── README.md │ │ ├── additional │ │ └── auto_generate.zip │ │ ├── auto_generate.exe │ │ ├── html │ │ ├── 404.html │ │ ├── APScanResults.json │ │ ├── ClientScanResults.json │ │ ├── ClientScanTime.json │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ ├── _config.yml │ │ ├── _i18n │ │ │ └── english.yml │ │ ├── _includes │ │ │ ├── footer.html │ │ │ ├── inline.js │ │ │ └── nav.html │ │ ├── _layouts │ │ │ ├── html.html │ │ │ └── page.html │ │ ├── dark.scss │ │ ├── index.html │ │ ├── info.html │ │ ├── js │ │ │ ├── functions.js │ │ │ ├── info.js │ │ │ ├── scan.js │ │ │ └── settings.js │ │ ├── main.scss │ │ ├── reloadSSID.json │ │ ├── restart.json │ │ ├── settings.html │ │ ├── settings.json │ │ ├── settingsReset.json │ │ ├── settingsSave.json │ │ └── sysinfo.json │ │ └── output │ │ └── gzip │ │ ├── dark.css.gz │ │ ├── error_404.html.gz │ │ ├── functions.js.gz │ │ ├── index.html.gz │ │ ├── info.html.gz │ │ ├── info.js.gz │ │ ├── main.css.gz │ │ ├── scan.js.gz │ │ ├── settings.html.gz │ │ └── settings.js.gz └── wifi_handler │ ├── CMakeLists.txt │ ├── component.mk │ ├── include │ ├── wifi_event_handler.h │ ├── wifi_handler.h │ └── wifi_init.h │ ├── wifi_event_handler.c │ ├── wifi_handler.c │ └── wifi_init.c ├── docs ├── Advanced_topics.md ├── Build_setup.md └── images │ ├── auto_generate.png │ ├── info.png │ ├── scan.png │ ├── settings.png │ ├── win_flash_full_bin.png │ └── win_flash_multi_bin.png ├── partitions.csv ├── platformio.ini ├── post_extra_script.py ├── pre_extra_script.py ├── sdkconfig.esp32dev ├── sdkconfig.esp32dev.old ├── src ├── CMakeLists.txt ├── Kconfig.projbuild ├── component.mk ├── esp32_nat_router.c └── include │ └── cmd_decl.h ├── start_web_server.bat └── start_web_server.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | !build/bootloader 3 | !build/esp32_nat_router.bin 4 | !build/partitions_example.bin 5 | .pio* 6 | .vscode 7 | .vscode/* 8 | !sdkconfig.* 9 | !sdkconfig.defaults 10 | components/web_server/www/output/html/ 11 | release/ 12 | pytest/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(esp32_nat_router+) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jaya Satish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | # PROJECT_NAME := esp32_nat_router 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-NAT Router + 2 | 3 | This Firmware that helps you use the ESP32 as a WiFi router. You can use it to do a few things, like make your existing WiFi signal stronger, create a new WiFi network for guests or smart devices, or simplify a more complicated network. It can handle data at a speed of more than 15mbps. 4 | 5 | ## Acknowledgments 6 | 7 | This project builds upon the works of *Martin-Ger's* [ESP32 NAT Router](https://github.com/martin-ger/esp32_nat_router) and *Sam Denty's* [Wi-PWN](https://github.com/samdenty/Wi-PWN) for the UI design. However, other sources have also provided inspiration and contributed to the development of this project, including: 8 | 9 | - [@Kevin Wolfe](https://gitlab.com/kevinwolfe) (who provided OTA and other useful resources) 10 | - [@nopnop2002](https://github.com/nopnop2002) (Cjson Doc and Examples) 11 | - [@dchristl](https://github.com/dchristl) (whose work and ideas) 12 | 13 | ## Features 14 | 15 | The features of the project, which includes: 16 | 17 | - Providing a user-friendly UI with mobile support for easy accessibility 18 | - Displaying connected states and quality in the UI for transparency 19 | - Scanning for available APs to enhance network connectivity 20 | - Allowing users to disable or enable the on-board indicator LED 21 | - Incorporating a button menu (accessible via Boot button) 22 | - Custom DNS server for personalized settings 23 | - Incorporating a secure web interface with basic authentication method 24 | - Offering OTA updates for easy maintenance and functionality enhancements 25 | - Randomize MAC address 26 | - Switch Between Dark and Light mode. (through settings) 27 | - Auto reload pages when connected to Router 28 | - MAC Filter for added security 29 | - and many more........... 30 | 31 | ## First Boot 32 | 33 | Upon the first boot of the ESP32 NAT Router, a WiFi network with an open AP and an ssid of "ESP32_NAT_Router +" will be available. Users can configure the device using either a simple web interface or the serial console. 34 | 35 | ## Web Config Interface 36 | 37 | The ESP32 NAT Router has a web interface that allows you to configure all its parameters. To access this interface, connect your PC or smartphone to the WiFi SSID "ESP32_NAT_Router +". Once you're connected, open your web browser and enter 38 | 39 | "[http://192.168.4.1](http://192.168.4.1/)''  into the address bar of your browser. Once you have entered this, you will be prompted for a username and password. The default `username is "admin" `and the default` password is "123456789"`. Please note that it is recommended to change these to a more secure username and password once you have logged in: 40 | 41 | 42 | 43 | To configure the uplink WiFi network, follow these steps: 44 | 45 | 1. Go to "Settings" in the web interface of the ESP32 NAT Router 46 | 2. Enter the appropriate values for the "STA Settings" section. If it is an open network, leave the password field blank 47 | 3. Change the "AP Settings" and other customizations as necessary 48 | 4. Click on the "Save" button to save the configuration 49 | 5. Press the "Reboot" button located at the bottom right corner of the page (Nessory) 50 | 6. Wait for the device to reboot 51 | 52 | Connect to the newly configured WiFi network with the updated settings. 53 | By following these steps, you can reconfigure the ESP32 NAT Router to connect to a new uplink WiFi network with customized settings based on your preferences. 54 | 55 | **Note:** The 5th step is necessary to update certain settings on the device and apply them. Skipping this step may result in the settings not being properly applied, which can lead to issues with the device's functionality. Therefore, it is recommended to always complete the 5th step when making any changes to the device's settings in order to ensure that the device is functioning as expected and that any desired changes have been properly applied. 56 | 57 | 61 | 62 | ## Flashing the prebuild binaries 63 | 64 | - Download [latest release](https://github.com/gjroots/esp32_nat_router_plus/releases/latest) 65 | * Download esp32nat_Router+_vX.X.X.zip for fresh install (multi bin) 66 | * Download esp32nat_Router+_full_vX.X.X_0x0.zip for single bin (merged, 0x0) 67 | - Install [esptool](https://github.com/espressif/esptool) 68 | 69 | ### First install/ Reset (Terminal/CMD) 70 | 71 | Please note that if your device was previously used for other projects, or if you want to reset all settings from a previous version, executing the following steps will result in complete data loss. 72 | 73 | Note: replace X.X.X with respective Version.(eg. ...._v1.0.0.bin) 74 | 75 | Unpack archive first and then execute: 76 | 77 | Single bin method (merged, 0x0): 78 | 79 | ``` 80 | esptool.py write_flash 0x0 esp32nat_Router+_full_vX.X.X_0x0.bin 81 | ``` 82 | 83 | Multi bin method: 84 | 85 | ``` 86 | esptool.py --chip esp32 \ 87 | --before default_reset --after hard_reset write_flash \ 88 | -z --flash_mode dio --flash_freq 40m --flash_size detect \ 89 | 0x1000 bootloader.bin \ 90 | 0x8000 partitions.bin \ 91 | 0x10000 esp32nat_Router+_vX.X.X.bin 92 | ``` 93 | 94 | ### Update from older version 95 | 96 | If this project was already installed. No data loss from previous version. 97 | 98 | ``` 99 | esptool.py write_flash 0x10000 esp32nat_Router+_vX.X.X.bin 100 | ``` 101 | 102 | If any problem occurs, erase flash manually before flashing: 103 | 104 | ``` 105 | esptool.py erase_flash 106 | ``` 107 | 108 | ### OTA method : 109 | 110 | To perform an OTA update on the ESP32-NATS Router+, follow these steps: 111 | 112 | 1. Go to the "Info" page in the web interface of the router 113 | 2. Click on the "Update" button 114 | 3. Select the desired bin file for the update 115 | 116 | Please note that the OTA method only supports updating with a single bin file, such as "esp32nat_Router+_vX.X.X.bin". Updating with a full bin (merged to single bin 0x0) is not supported. 117 | 118 | ### Alternative way/ Graphical (Windows only) 119 | 120 | As an alternative you might use [Espressif's Flash Download Tools](https://www.espressif.com/en/support/download/other-tools). 121 | Check the marked parameters and files like below (ckeck the COM-Port for your environment). 122 | Check the addresses like below: 123 | 124 | ![Single bin](docs/images/win_flash_full_bin.png) 125 | 126 | ### or 127 | 128 | ![multiple bin's](docs/images/win_flash_multi_bin.png) 129 | 130 | ## Building the Binaries 131 | 132 | To build the binaries, please refer to the following guide: "[How to setup environment and build project](docs/Build_setup.md)" located in the "docs" folder. This guide outlines the necessary steps to set up the environment and build the binaries. 133 | 134 | ## Advanced topics and configuration 135 | 136 | For more advanced configuration and topics, refer to the "[Advanced topics](docs/Advanced_topics.md)" guide located in the "docs" folder. This guide covers topics such as configuring wireless protocols, setting up custom DNS servers working with OTA and other settings. 137 | 138 | ## Misc 139 | 140 | If you experience any issues or have any suggestions for new features, please feel free to ask or raise an issue. Please note that as this project is a spare time project, the creator may not be always available to answer queries promptly. However, if the creator is free, they will respond as soon as possible. Thank you for your understanding and patience. 141 | 142 | If you are happy with the project and would like to support the creator, there are several ways to do so. One way is to contribute to the codebase or documentation. Alternatively, you can support the creator through by spreading the word about the project to others who may find it useful. 143 | 144 | ## License 145 | 146 | The software is released under the **MIT License** , which can be found in the file named "[LICENSE](LICENSE)". Additionally, any third-party source files included in the software will have their own license header. 147 | 148 | ## Screenshots 149 | 150 | [scan.png](docs/images/scan.png) 151 | 152 | [settings.png](docs/images/settings.png) 153 | 154 | [info.png](docs/images/info.png) 155 | -------------------------------------------------------------------------------- /build_web_pages.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd components\web_server\www\html 3 | echo Current directory: %cd% 4 | echo Site Building Started 5 | 6 | ::this command enables to minified version site files 7 | set JEKYLL_ENV=production 8 | 9 | ::Execute Building Site 10 | bundle exec jekyll build 11 | 12 | echo Site Building completed -------------------------------------------------------------------------------- /build_web_pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd components\\web_server\\www\\html 3 | pwd 4 | echo Site Building Started 5 | 6 | # this command enables to minified version site files 7 | JEKYLL_ENV=production # comment out if not require 8 | 9 | # this command execute Building Site 10 | bundle exec jekyll build 11 | 12 | echo Site Building completed 13 | -------------------------------------------------------------------------------- /components/cmd_nvs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "cmd_nvs.c" 2 | INCLUDE_DIRS . 3 | REQUIRES console nvs_flash) -------------------------------------------------------------------------------- /components/cmd_nvs/cmd_nvs.h: -------------------------------------------------------------------------------- 1 | /* Console example — declarations of command registration functions. 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | // Register NVS functions 16 | void register_nvs(void); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /components/cmd_nvs/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/cmd_router/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "cmd_router.c" 2 | INCLUDE_DIRS . 3 | REQUIRES console esp_wifi driver nvs_flash) 4 | -------------------------------------------------------------------------------- /components/cmd_router/cmd_router.h: -------------------------------------------------------------------------------- 1 | /* Console example — various router commands 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | // Register router functions 16 | void register_router(void); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /components/cmd_router/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/cmd_router/router_globals.h: -------------------------------------------------------------------------------- 1 | /* Various global declarations for the esp32_nat_router 2 | 3 | Unless required by applicable law or agreed to in writing, this 4 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 5 | CONDITIONS OF ANY KIND, either express or implied. 6 | */ 7 | #pragma once 8 | 9 | #ifdef __cplusplus 10 | extern "C" 11 | { 12 | #endif 13 | 14 | #define PARAM_NAMESPACE "esp32_nat" 15 | #include "lwip/ip4_addr.h" 16 | #define PROTO_TCP 6 17 | #define PROTO_UDP 17 18 | 19 | extern char *ssid; 20 | extern char *ent_username; 21 | extern char *ent_identity; 22 | extern char *passwd; 23 | extern char *static_ip; 24 | extern char *subnet_mask; 25 | extern char *gateway_addr; 26 | extern char *ap_ssid; 27 | extern char *ap_passwd; 28 | extern char *ap_ip; 29 | 30 | extern bool scanning_started; 31 | 32 | extern uint16_t connect_count; 33 | extern bool ap_connect; 34 | 35 | extern uint32_t my_ip; 36 | extern uint32_t my_ap_ip; 37 | 38 | void preprocess_string(char *str); 39 | int set_sta(int argc, char **argv); 40 | int set_sta_static(int argc, char **argv); 41 | int set_ap(int argc, char **argv); 42 | 43 | esp_err_t get_config_param_int(char *name, int *param); 44 | esp_err_t get_config_param_str(char *name, char **param); 45 | 46 | void print_portmap_tab(); 47 | esp_err_t add_portmap(u8_t proto, u16_t mport, u32_t daddr, u16_t dport); 48 | esp_err_t del_portmap(u8_t proto, u16_t mport); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /components/cmd_system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "cmd_system.c" 2 | INCLUDE_DIRS . 3 | REQUIRES console driver esp_wifi spi_flash esp_app_format) -------------------------------------------------------------------------------- /components/cmd_system/cmd_system.h: -------------------------------------------------------------------------------- 1 | /* Console example — various system commands 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | // Register system functions 16 | void register_system(void); 17 | void determineChipType(char chip_type[30]); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /components/cmd_system/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/console_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS console_handler.c file_system.c 2 | 3 | INCLUDE_DIRS include 4 | REQUIRES cmd_router cmd_nvs nvs_flash fatfs json vfs esp_driver_uart esp_driver_usb_serial_jtag) -------------------------------------------------------------------------------- /components/console_handler/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/console_handler/console_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "driver/uart_vfs.h" 17 | #include "driver/usb_serial_jtag_vfs.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "esp_sleep.h" 23 | #include "esp_vfs_dev.h" 24 | #include "driver/usb_serial_jtag.h" 25 | #include "router_globals.h" 26 | #include "console_handler.h" 27 | 28 | void initialize_console(void) 29 | { 30 | /* Drain stdout before reconfiguring it */ 31 | fflush(stdout); 32 | fsync(fileno(stdout)); 33 | 34 | /* Disable buffering on stdin */ 35 | setvbuf(stdin, NULL, _IONBF, 0); 36 | 37 | /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ 38 | uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); 39 | /* Move the caret to the beginning of the next line on '\n' */ 40 | uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); 41 | 42 | /* Configure UART. Note that REF_TICK is used so that the baud rate remains 43 | * correct while APB frequency is changing in light sleep mode. 44 | */ 45 | const uart_config_t uart_config = { 46 | .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, 47 | .data_bits = UART_DATA_8_BITS, 48 | .parity = UART_PARITY_DISABLE, 49 | .stop_bits = UART_STOP_BITS_1, 50 | #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) 51 | .source_clk = UART_SCLK_REF_TICK, 52 | #else 53 | .source_clk = UART_SCLK_XTAL, 54 | #endif 55 | }; 56 | /* Install UART driver for interrupt-driven reads and writes */ 57 | ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 58 | 256, 0, 0, NULL, 0)); 59 | ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config)); 60 | 61 | /* Tell VFS to use UART driver */ 62 | uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); 63 | 64 | /* Initialize the console */ 65 | esp_console_config_t console_config = { 66 | .max_cmdline_args = 8, 67 | .max_cmdline_length = 256, 68 | #if CONFIG_LOG_COLORS 69 | .hint_color = atoi(LOG_COLOR_CYAN) 70 | #endif 71 | }; 72 | ESP_ERROR_CHECK(esp_console_init(&console_config)); 73 | 74 | /* Configure linenoise line completion library */ 75 | /* Enable multiline editing. If not set, long commands will scroll within 76 | * single line. 77 | */ 78 | linenoiseSetMultiLine(1); 79 | 80 | /* Tell linenoise where to get command completions and hints */ 81 | linenoiseSetCompletionCallback(&esp_console_get_completion); 82 | linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint); 83 | 84 | /* Set command history size */ 85 | linenoiseHistorySetMaxLen(100); 86 | 87 | #if CONFIG_STORE_HISTORY 88 | /* Load command history from filesystem */ 89 | linenoiseHistoryLoad(HISTORY_PATH); 90 | #endif 91 | 92 | /* Register commands */ 93 | esp_console_register_help_command(); 94 | } 95 | 96 | 97 | void start_console(void) 98 | { 99 | /* Prompt to be printed before each line. 100 | * This can be customized, made dynamic, etc. 101 | */ 102 | const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR; 103 | 104 | printf("\n" 105 | "ESP32 NAT ROUTER\n" 106 | "Type 'help' to get the list of commands.\n" 107 | "Use UP/DOWN arrows to navigate through command history.\n" 108 | "Press TAB when typing command name to auto-complete.\n"); 109 | 110 | if (strlen(ssid) == 0) 111 | { 112 | printf("\n" 113 | "Unconfigured WiFi\n" 114 | "Configure using 'set_sta' and 'set_ap' and restart.\n"); 115 | } 116 | 117 | /* Figure out if the terminal supports escape sequences */ 118 | int probe_status = linenoiseProbe(); 119 | if (probe_status) 120 | { /* zero indicates success */ 121 | printf("\n" 122 | "Your terminal application does not support escape sequences.\n" 123 | "Line editing and history features are disabled.\n" 124 | "On Windows, try using Putty instead.\n"); 125 | linenoiseSetDumbMode(1); 126 | #if CONFIG_LOG_COLORS 127 | /* Since the terminal doesn't support escape sequences, 128 | * don't use color codes in the prompt. 129 | */ 130 | prompt = "esp32> "; 131 | #endif // CONFIG_LOG_COLORS 132 | } 133 | 134 | /* Main loop */ 135 | while (true) 136 | { 137 | /* Get a line using linenoise. 138 | * The line is returned when ENTER is pressed. 139 | */ 140 | char *line = linenoise(prompt); 141 | if (line == NULL) 142 | { /* Ignore empty lines */ 143 | continue; 144 | } 145 | /* Add the command to the history */ 146 | linenoiseHistoryAdd(line); 147 | #if CONFIG_STORE_HISTORY 148 | /* Save command history to filesystem */ 149 | linenoiseHistorySave(HISTORY_PATH); 150 | #endif 151 | 152 | /* Try to run the command */ 153 | int ret; 154 | esp_err_t err = esp_console_run(line, &ret); 155 | if (err == ESP_ERR_NOT_FOUND) 156 | { 157 | printf("Unrecognized command\n"); 158 | } 159 | else if (err == ESP_ERR_INVALID_ARG) 160 | { 161 | // command was empty 162 | } 163 | else if (err == ESP_OK && ret != ESP_OK) 164 | { 165 | printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); 166 | } 167 | else if (err != ESP_OK) 168 | { 169 | printf("Internal error: %s\n", esp_err_to_name(err)); 170 | } 171 | /* linenoise allocates line buffer on the heap, so need to free it */ 172 | linenoiseFree(line); 173 | } 174 | } -------------------------------------------------------------------------------- /components/console_handler/file_system.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "router_globals.h" 17 | #include "console_handler.h" 18 | 19 | static const char *TAG = "Console/file_system"; 20 | 21 | /* Console command history can be stored to and loaded from a file. 22 | * The easiest way to do this is to use FATFS filesystem on top of 23 | * wear_levelling library. 24 | */ 25 | #if CONFIG_STORE_HISTORY 26 | 27 | void initialize_filesystem(void) 28 | { 29 | static wl_handle_t wl_handle; 30 | const esp_vfs_fat_mount_config_t mount_config = { 31 | .max_files = 4, 32 | .format_if_mount_failed = true}; 33 | esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(MOUNT_PATH, "storage", &mount_config, &wl_handle); 34 | if (err != ESP_OK) 35 | { 36 | ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); 37 | return; 38 | } 39 | } 40 | #endif // CONFIG_STORE_HISTORY -------------------------------------------------------------------------------- /components/console_handler/include/console_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #define MOUNT_PATH "/data" 17 | #define HISTORY_PATH MOUNT_PATH "/history.txt" 18 | 19 | void initialize_console(void); 20 | void start_console(void); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /components/console_handler/include/file_system.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | void initialize_filesystem(void); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /components/hardware_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS hardware_handler.c led_handler.c 2 | button_handler.c 3 | INCLUDE_DIRS include 4 | REQUIRES cmd_router utils web_server) -------------------------------------------------------------------------------- /components/hardware_handler/button_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #include "button_handler.h" 10 | #include "hardware_handler.h" 11 | #include "router_globals.h" 12 | #include "led_handler.h" 13 | #include "initialization.h" 14 | #include "utils.h" 15 | #include "nvm.h" 16 | #include "web_server.h" 17 | 18 | 19 | int menu_count = 0; 20 | bool IsMenuEnable = false; 21 | 22 | 23 | //----------------------------------------------------------------------------- 24 | void menu_count_reset(void) 25 | { 26 | menu_count = -1; 27 | } 28 | 29 | 30 | //----------------------------------------------------------------------------- 31 | void selected_menu_item(void) 32 | { 33 | switch (menu_count) 34 | { 35 | case 0: 36 | printf("selected menu mode\n"); 37 | IsMenuEnable = true; 38 | break; 39 | case 1: 40 | printf("selected menu: 1\n"); 41 | IsLedEnable = false; 42 | item_selected_success(); 43 | break; 44 | case 2: 45 | printf("selected menu: 2\n"); 46 | toggle_webserver(); 47 | item_selected_success(); 48 | break; 49 | case 3: 50 | printf("selected menu: 3\n"); 51 | if (nvm_erase() == ESP_OK) 52 | { 53 | item_selected_success(); 54 | vTaskDelay(2000 / portTICK_PERIOD_MS); 55 | restart(); 56 | } 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | 63 | 64 | //----------------------------------------------------------------------------- 65 | void button_task(void *pvParameters) 66 | { 67 | static float menu_reset_time_s = 0; 68 | while (1) 69 | { 70 | static float button_down_time_s = 0; 71 | vTaskDelay(100 / portTICK_PERIOD_MS); 72 | bool button_pressed = hardware_user_button_pressed(); 73 | if (button_pressed) 74 | { 75 | if (button_down_time_s == 0) 76 | { 77 | printf("Button pressed!\n"); 78 | menu_reset_time_s = 0; 79 | button_down_time_s = system_uptime_s(); 80 | menu_reset_time_s = system_uptime_s(); 81 | } 82 | else if (system_uptime_s() > (button_down_time_s + 3)) 83 | { 84 | button_down_time_s += (60 * 60); 85 | selected_menu_item(); 86 | menu_count_reset(); 87 | } 88 | } 89 | else if ((system_uptime_s() > (menu_reset_time_s + 15)) && (IsMenuEnable)) 90 | { 91 | printf("Button menu time out\n"); 92 | menu_reset_time_s = 0; 93 | menu_count = 0; 94 | IsMenuEnable = false; 95 | } 96 | else if (button_down_time_s) 97 | { 98 | // printf("Button released!\n"); 99 | button_down_time_s = 0; 100 | if (menu_count > 2 || !IsMenuEnable) 101 | { 102 | menu_count = 0; 103 | IsMenuEnable = false; 104 | } 105 | else 106 | { 107 | menu_count++; 108 | } 109 | printf("menu number : %d \n", menu_count); 110 | } 111 | } 112 | } 113 | 114 | 115 | //----------------------------------------------------------------------------- 116 | void start_button_task(void) 117 | { 118 | xTaskCreate(button_task, "Button_task", configMINIMAL_STACK_SIZE * 3, NULL, 1, NULL); 119 | } 120 | 121 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/hardware_handler/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/hardware_handler/hardware_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | #include "hardware_handler.h" 12 | #include "utils.h" 13 | #include "led_handler.h" 14 | #include "button_handler.h" 15 | #include "router_globals.h" 16 | 17 | #define PIN_BUTTON (0) 18 | #define PIN_LED (2) 19 | 20 | static bool led_on = false; 21 | 22 | 23 | //----------------------------------------------------------------------------- 24 | bool hardware_user_button_pressed(void) 25 | { 26 | return !gpio_get_level(PIN_BUTTON); 27 | } 28 | 29 | //----------------------------------------------------------------------------- 30 | void hardware_turn_on_led(void) 31 | { 32 | gpio_set_level(PIN_LED, 1); 33 | led_on = true; 34 | } 35 | 36 | //----------------------------------------------------------------------------- 37 | void hardware_turn_off_led(void) 38 | { 39 | gpio_set_level(PIN_LED, 0); 40 | led_on = false; 41 | } 42 | 43 | //----------------------------------------------------------------------------- 44 | void hardware_toggle_led(void) 45 | { 46 | led_on = !led_on; 47 | gpio_set_level(PIN_LED, led_on); 48 | } 49 | 50 | //----------------------------------------------------------------------------- 51 | void hardware_init() 52 | { 53 | gpio_config_t io_conf; 54 | io_conf.pin_bit_mask = (1ULL << PIN_BUTTON); 55 | io_conf.mode = GPIO_MODE_INPUT; 56 | io_conf.intr_type = GPIO_INTR_DISABLE; 57 | io_conf.pull_up_en = 1; 58 | io_conf.pull_down_en = 0; 59 | gpio_config(&io_conf); 60 | 61 | io_conf.pin_bit_mask = (1ULL << PIN_LED); 62 | io_conf.mode = GPIO_MODE_OUTPUT; 63 | gpio_set_level(PIN_LED, led_on); 64 | gpio_config(&io_conf); 65 | 66 | start_button_task(); 67 | start_led_status_task(); 68 | } 69 | -------------------------------------------------------------------------------- /components/hardware_handler/include/button_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | void start_button_task(void); 18 | extern int menu_count; 19 | extern bool IsMenuEnable; 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif -------------------------------------------------------------------------------- /components/hardware_handler/include/hardware_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #ifndef HARDWARE_HANDLER_H 9 | #define HARDWARE_HANDLER_H 10 | 11 | #endif 12 | 13 | #pragma once 14 | 15 | #ifdef __cplusplus 16 | extern "C" 17 | { 18 | #endif 19 | 20 | #include 21 | #include 22 | 23 | void hardware_init(); 24 | bool hardware_user_button_pressed(void); 25 | void hardware_turn_on_led(void); 26 | void hardware_turn_off_led(void); 27 | void hardware_toggle_led(void); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /components/hardware_handler/include/led_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | #include "stdbool.h" 16 | 17 | extern bool IsLedEnable; 18 | void start_led_status_task(void); 19 | void item_selected_success(void); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif -------------------------------------------------------------------------------- /components/hardware_handler/led_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include "led_handler.h" 9 | #include "hardware_handler.h" 10 | #include "router_globals.h" 11 | #include "button_handler.h" 12 | 13 | //----------------------------------------------------------------------------- 14 | void item_selected_success(void) 15 | { 16 | 17 | for (int i = 0; i < 4; i++) 18 | { 19 | hardware_toggle_led(); 20 | vTaskDelay(50 / portTICK_PERIOD_MS); 21 | hardware_toggle_led(); 22 | vTaskDelay(50 / portTICK_PERIOD_MS); 23 | } 24 | IsMenuEnable = false; 25 | } 26 | 27 | //----------------------------------------------------------------------------- 28 | void led_status_task(void *pvParameters) 29 | { 30 | while (1) 31 | { 32 | if (IsLedEnable || IsMenuEnable) 33 | { 34 | if (IsMenuEnable) 35 | { 36 | hardware_turn_on_led(); 37 | 38 | // Blinking pattern for menu mode 39 | for (int i = 0; i < menu_count; i++) 40 | { 41 | hardware_toggle_led(); 42 | vTaskDelay(100 / portTICK_PERIOD_MS); 43 | hardware_toggle_led(); 44 | vTaskDelay(200 / portTICK_PERIOD_MS); 45 | } 46 | } 47 | else 48 | { 49 | (ap_connect) ? hardware_turn_on_led() : hardware_turn_off_led(); 50 | 51 | // Blinking pattern for client mode 52 | for (int i = 0; i < connect_count; i++) 53 | { 54 | hardware_toggle_led(); 55 | vTaskDelay(50 / portTICK_PERIOD_MS); 56 | hardware_toggle_led(); 57 | vTaskDelay(50 / portTICK_PERIOD_MS); 58 | } 59 | } 60 | 61 | vTaskDelay(1000 / portTICK_PERIOD_MS); 62 | } 63 | else 64 | { 65 | hardware_turn_off_led(); 66 | vTaskDelay(100 / portTICK_PERIOD_MS); 67 | } 68 | } 69 | } 70 | 71 | //----------------------------------------------------------------------------- 72 | void start_led_status_task(void) 73 | { 74 | xTaskCreate(led_status_task, "led_status_task", configMINIMAL_STACK_SIZE * 3, NULL, 1, NULL); 75 | } 76 | 77 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/ota_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "ota_handler.c" 2 | INCLUDE_DIRS include 3 | REQUIRES esp_http_server app_update cmd_router esp_eth) 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /components/ota_handler/README.md: -------------------------------------------------------------------------------- 1 | # ESP32-NAT Router + 2 | 3 | ## OTA Handler Component 4 | 5 | The OTA Handler is a firmware updater, that allows you to update ESP32 device over the air. It uses the HTTP POST method to upload the new firmware, making it easy to update devices without needing physical access. 6 | 7 | ## Usage 8 | 9 | To use the OTA Handler, you need to include the `ota_update_handler` function in your code. Here's an example of how to use it: 10 | 11 | ```c 12 | #include "ota_handler.h" 13 | 14 | // Run initial in main() 15 | ota_update_init() 16 | 17 | ................ 18 | //send Uploding post request to this function 19 | ota_update_handler(req); 20 | ``` 21 | 22 | In `CMakeLists.txt` update `REQUIRES ota_handler` 23 | 24 | The `ota_update_handler` function checks if the firmware binary is present in the request body, verifies that it's valid, and updates the firmware if everything is correct. If the update is successful, it sends a response indicating that the firmware update was successful. 25 | 26 | In addition to the `ota_update_task(void *Param)` function, the OTA Handler also provides a task that can be used for continuous checking. If you need to perform continuous checking for firmware updates and also if problem occur it will restore back to old version. 27 | 28 | ## Credit's 29 | 30 | - The OTA update code was inspired by [@Kecin Wolfe](https://gitlab.com/kevinwolfe/esp32_template)'s implementation. 31 | -------------------------------------------------------------------------------- /components/ota_handler/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/ota_handler/include/ota_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #pragma once 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | #include 15 | #include 16 | #include "esp_http_server.h" 17 | 18 | void ota_update_init(void); 19 | esp_err_t ota_update_handler(httpd_req_t *req); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif -------------------------------------------------------------------------------- /components/ota_handler/ota_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "nvs_flash.h" 15 | #include "esp_netif.h" 16 | #include "esp_eth.h" 17 | #include "esp_ota_ops.h" 18 | #include "esp_flash_partitions.h" 19 | #include "esp_partition.h" 20 | #include "router_globals.h" 21 | #include "ota_handler.h" 22 | 23 | static const char *TAG = "OTA handler"; 24 | 25 | //----------------------------------------------------------------------------- 26 | esp_err_t ota_update_handler(httpd_req_t *req) 27 | { 28 | 29 | esp_err_t err; 30 | char buf[256]; 31 | int remaining = req->content_len; 32 | esp_ota_handle_t update_handle = 0; 33 | const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); 34 | const esp_partition_t *running_partition = esp_ota_get_running_partition(); 35 | 36 | if (!update_partition) 37 | { 38 | ESP_LOGE(TAG, "Failed to get update partition"); 39 | return ESP_FAIL; 40 | } 41 | 42 | ESP_LOGI(TAG, "Writing partition: type %d, subtype %d, offset 0x%08lx\n", 43 | update_partition->type, update_partition->subtype, update_partition->address); 44 | ESP_LOGI(TAG, "Running partition: type %d, subtype %d, offset 0x%08lx\n", 45 | running_partition->type, running_partition->subtype, running_partition->address); 46 | 47 | err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle); 48 | if (err != ESP_OK) 49 | { 50 | ESP_LOGE(TAG, "Failed to begin OTA (%s)", esp_err_to_name(err)); 51 | return err; 52 | } 53 | 54 | while (remaining > 0) 55 | { 56 | int ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf))); 57 | if (ret <= 0) 58 | { 59 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) 60 | { 61 | continue; 62 | } 63 | ESP_LOGE(TAG, "Failed to receive OTA data (%s)", esp_err_to_name(ret)); 64 | esp_ota_abort(update_handle); 65 | return ESP_FAIL; 66 | } 67 | remaining -= ret; 68 | err = esp_ota_write(update_handle, buf, ret); 69 | if (err != ESP_OK) 70 | { 71 | ESP_LOGE(TAG, "Failed to write OTA data (%s)", esp_err_to_name(err)); 72 | esp_ota_abort(update_handle); 73 | return ESP_FAIL; 74 | } 75 | } 76 | 77 | err = esp_ota_end(update_handle); 78 | if (err != ESP_OK) 79 | { 80 | ESP_LOGE(TAG, "Failed to end OTA (%s)", esp_err_to_name(err)); 81 | esp_ota_abort(update_handle); 82 | return ESP_FAIL; 83 | } 84 | 85 | err = esp_ota_set_boot_partition(update_partition); 86 | if (err != ESP_OK) 87 | { 88 | ESP_LOGE(TAG, "Failed to set boot partition (%s)", esp_err_to_name(err)); 89 | esp_ota_abort(update_handle); 90 | return ESP_FAIL; 91 | } 92 | 93 | ESP_LOGI(TAG, "OTA Success! Rebooting\n"); 94 | 95 | httpd_resp_set_status(req, HTTPD_200); 96 | httpd_resp_send(req, NULL, 0); 97 | 98 | vTaskDelay(2000 / portTICK_PERIOD_MS); 99 | esp_restart(); 100 | 101 | return ESP_OK; 102 | } 103 | 104 | //----------------------------------------------------------------------------- 105 | /* handling ota update process */ 106 | void ota_update_task(void *Param) 107 | { 108 | const esp_partition_t *running = esp_ota_get_running_partition(); 109 | esp_ota_img_states_t ota_state; 110 | if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) 111 | { 112 | if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) 113 | { 114 | // Validate image some how, then call: 115 | esp_ota_mark_app_valid_cancel_rollback(); 116 | // If needed: esp_ota_mark_app_invalid_rollback_and_reboot(); 117 | } 118 | } 119 | 120 | const uint32_t task_delay_ms = 10; 121 | while (1) 122 | { 123 | vTaskDelay(task_delay_ms / portTICK_PERIOD_MS); 124 | } 125 | } 126 | 127 | void ota_update_init(void) 128 | { 129 | xTaskCreate(ota_update_task, "ota_update_task", configMINIMAL_STACK_SIZE * 3, NULL, 1, NULL); 130 | } -------------------------------------------------------------------------------- /components/router_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS router_handler.c 2 | 3 | INCLUDE_DIRS include 4 | REQUIRES cmd_router utils) -------------------------------------------------------------------------------- /components/router_handler/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/router_handler/include/router_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | esp_err_t get_portmap_tab(); 18 | esp_err_t apply_portmap_tab(); 19 | esp_err_t delete_portmap_tab(); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /components/router_handler/router_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "router_globals.h" 26 | #include "initialization.h" 27 | #include "router_handler.h" 28 | 29 | 30 | static const char *TAG = "Router_handler"; 31 | 32 | struct portmap_table_entry 33 | { 34 | u32_t daddr; 35 | u16_t mport; 36 | u16_t dport; 37 | u8_t proto; 38 | u8_t valid; 39 | }; 40 | struct portmap_table_entry portmap_tab[IP_PORTMAP_MAX]; 41 | 42 | 43 | //----------------------------------------------------------------------------- 44 | esp_err_t apply_portmap_tab() 45 | { 46 | for (int i = 0; i < IP_PORTMAP_MAX; i++) 47 | { 48 | if (portmap_tab[i].valid) 49 | { 50 | ip_portmap_add(portmap_tab[i].proto, my_ip, portmap_tab[i].mport, portmap_tab[i].daddr, portmap_tab[i].dport); 51 | } 52 | } 53 | return ESP_OK; 54 | } 55 | 56 | 57 | //----------------------------------------------------------------------------- 58 | esp_err_t delete_portmap_tab() 59 | { 60 | for (int i = 0; i < IP_PORTMAP_MAX; i++) 61 | { 62 | if (portmap_tab[i].valid) 63 | { 64 | ip_portmap_remove(portmap_tab[i].proto, portmap_tab[i].mport); 65 | } 66 | } 67 | return ESP_OK; 68 | } 69 | 70 | 71 | //----------------------------------------------------------------------------- 72 | void print_portmap_tab() 73 | { 74 | for (int i = 0; i < IP_PORTMAP_MAX; i++) 75 | { 76 | if (portmap_tab[i].valid) 77 | { 78 | printf("%s", portmap_tab[i].proto == PROTO_TCP ? "TCP " : "UDP "); 79 | ip4_addr_t addr; 80 | addr.addr = my_ip; 81 | printf(IPSTR ":%d -> ", IP2STR(&addr), portmap_tab[i].mport); 82 | addr.addr = portmap_tab[i].daddr; 83 | printf(IPSTR ":%d\n", IP2STR(&addr), portmap_tab[i].dport); 84 | } 85 | } 86 | } 87 | 88 | 89 | //----------------------------------------------------------------------------- 90 | esp_err_t get_portmap_tab() 91 | { 92 | esp_err_t err; 93 | nvs_handle_t nvs; 94 | size_t len; 95 | 96 | err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs); 97 | if (err != ESP_OK) 98 | { 99 | return err; 100 | } 101 | err = nvs_get_blob(nvs, "portmap_tab", NULL, &len); 102 | if (err == ESP_OK) 103 | { 104 | if (len != sizeof(portmap_tab)) 105 | { 106 | err = ESP_ERR_NVS_INVALID_LENGTH; 107 | } 108 | else 109 | { 110 | err = nvs_get_blob(nvs, "portmap_tab", portmap_tab, &len); 111 | if (err != ESP_OK) 112 | { 113 | memset(portmap_tab, 0, sizeof(portmap_tab)); 114 | } 115 | } 116 | } 117 | nvs_close(nvs); 118 | 119 | return err; 120 | } 121 | 122 | 123 | //----------------------------------------------------------------------------- 124 | esp_err_t add_portmap(u8_t proto, u16_t mport, u32_t daddr, u16_t dport) 125 | { 126 | esp_err_t err; 127 | nvs_handle_t nvs; 128 | 129 | for (int i = 0; i < IP_PORTMAP_MAX; i++) 130 | { 131 | if (!portmap_tab[i].valid) 132 | { 133 | portmap_tab[i].proto = proto; 134 | portmap_tab[i].mport = mport; 135 | portmap_tab[i].daddr = daddr; 136 | portmap_tab[i].dport = dport; 137 | portmap_tab[i].valid = 1; 138 | 139 | err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs); 140 | if (err != ESP_OK) 141 | { 142 | return err; 143 | } 144 | err = nvs_set_blob(nvs, "portmap_tab", portmap_tab, sizeof(portmap_tab)); 145 | if (err == ESP_OK) 146 | { 147 | err = nvs_commit(nvs); 148 | if (err == ESP_OK) 149 | { 150 | ESP_LOGI(TAG, "New portmap table stored."); 151 | } 152 | } 153 | nvs_close(nvs); 154 | 155 | ip_portmap_add(proto, my_ip, mport, daddr, dport); 156 | 157 | return ESP_OK; 158 | } 159 | } 160 | return ESP_ERR_NO_MEM; 161 | } 162 | 163 | 164 | //----------------------------------------------------------------------------- 165 | esp_err_t del_portmap(u8_t proto, u16_t mport) 166 | { 167 | esp_err_t err; 168 | nvs_handle_t nvs; 169 | 170 | for (int i = 0; i < IP_PORTMAP_MAX; i++) 171 | { 172 | if (portmap_tab[i].valid && portmap_tab[i].mport == mport && portmap_tab[i].proto == proto) 173 | { 174 | portmap_tab[i].valid = 0; 175 | 176 | err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs); 177 | if (err != ESP_OK) 178 | { 179 | return err; 180 | } 181 | err = nvs_set_blob(nvs, "portmap_tab", portmap_tab, sizeof(portmap_tab)); 182 | if (err == ESP_OK) 183 | { 184 | err = nvs_commit(nvs); 185 | if (err == ESP_OK) 186 | { 187 | ESP_LOGI(TAG, "New portmap table stored."); 188 | } 189 | } 190 | nvs_close(nvs); 191 | 192 | ip_portmap_remove(proto, mport); 193 | return ESP_OK; 194 | } 195 | } 196 | return ESP_OK; 197 | } 198 | 199 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS utils.c initialization.c nvm.c mac_generator.c mac_filter.c 2 | 3 | INCLUDE_DIRS include 4 | REQUIRES cmd_router cmd_nvs nvs_flash json esp_http_server 5 | driver esp_hw_support esp_timer efuse) -------------------------------------------------------------------------------- /components/utils/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/utils/include/initialization.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include "esp_err.h" 19 | #define MAX_LOGIN_ATTEMPTS (5) 20 | #define LOCK_OUT_TIME_MINUTES (1) // MINUTES 21 | #define DEFAULT_AP_IP "192.168.4.1" 22 | #define DEFAULT_DNS1 "1.1.1.1" 23 | #define DEFAULT_ADMIN_USERNAME "admin" 24 | #define DEFAULT_ADMIN_PASSWORD "123456789" 25 | #define DEFAULT_SSID "ESP32_NAT_Router +" 26 | 27 | extern bool IsLedEnable, IsWebServerEnable, IsCustomDnsEnable, 28 | IsRandomizeMacEnable, IsDarkModeEnable, IsWifiAuthFail, IsMacFilterEnable, IsAllowList; 29 | 30 | extern char *customDNSip, *authUsername, 31 | *authPass, *macAp, *dnsIP, *cache; 32 | extern char currentMAC[18]; 33 | extern int max_login_attempts, lock_out_time_minutes; 34 | esp_err_t parms_init(); 35 | 36 | typedef struct 37 | { 38 | char *username; 39 | char *password; 40 | } basic_auth_info_t; 41 | 42 | extern basic_auth_info_t auth_info; 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /components/utils/include/mac_filter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | #include 16 | #include 17 | #include 18 | 19 | #define MAX_MAC_ADDRESSES 10 20 | #define NVS_KEY "stored_macs" 21 | 22 | 23 | esp_err_t store_mac_address_in_nvs(const char *macAddress); 24 | esp_err_t remove_mac_address_from_nvs(const char *macAddress); 25 | bool check_mac_address_in_nvs(const char *macAddress); 26 | cJSON *retrieve_mac_addresses_as_json(); 27 | void mac_filter(const uint8_t mac[6],uint8_t aid); 28 | void refresh_mac_filter(); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /components/utils/include/mac_generator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | void custom_mac_generator(void); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /components/utils/include/nvm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include "esp_err.h" 17 | 18 | esp_err_t nvm_erase(void); 19 | esp_err_t nvm_set_bool(const char *key, bool value); 20 | void initialize_nvs(void); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /components/utils/include/utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef ICACHE_FLASH 21 | #define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text"))) 22 | #define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) 23 | #else 24 | #define ICACHE_FLASH_ATTR 25 | #define ICACHE_RODATA_ATTR 26 | #endif /* ICACHE_FLASH */ 27 | 28 | float system_uptime_s(void); 29 | void restart(void); 30 | 31 | bool is_valid_ip(const char *ip_str); 32 | bool str_to_bool(const char *key); 33 | int bool_to_int(bool value); 34 | bool word_check(const char *str, const char *word); 35 | char *html_escape(const char *src); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /components/utils/initialization.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include "initialization.h" 12 | #include "router_globals.h" 13 | #include "mac_generator.h" 14 | 15 | static const char *TAG = "utils/initialization"; 16 | 17 | bool IsLedEnable = true, IsWebServerEnable = true, 18 | IsCustomDnsEnable = false, IsRandomizeMacEnable = false, 19 | IsDarkModeEnable = false, IsWifiAuthFail = false, 20 | IsMacFilterEnable = false, IsAllowList = false; 21 | 22 | char *ssid, *ent_username, *ent_identity, 23 | *passwd, *static_ip, *subnet_mask, *gateway_addr, 24 | *ap_ssid, *ap_passwd, *ap_ip, *customDNSip, *authUsername, 25 | *authPass, *macAp, *dnsIP = ""; 26 | 27 | char currentMAC[18]; 28 | 29 | //----------------------------------------------------------------------------- 30 | esp_err_t IRAM_ATTR parms_init() 31 | { 32 | int webServer = 1, ledEnable = 1, customDnsEnable = 0, 33 | darkModeEnable = 0, randomizeMac = 0, isMacFilterEnable = 0, isAllowList = 0; 34 | 35 | // ESP_LOGW(TAG, "initialization Started"); 36 | get_config_param_str("ssid", &ssid); 37 | get_config_param_str("passwd", &passwd); 38 | get_config_param_str("ent_username", &ent_username); 39 | get_config_param_str("ent_identity", &ent_identity); 40 | get_config_param_str("ap_ssid", &ap_ssid); 41 | get_config_param_str("ap_passwd", &ap_passwd); 42 | get_config_param_str("static_ip", &static_ip); 43 | get_config_param_str("subnet_mask", &subnet_mask); 44 | get_config_param_str("gateway_addr", &gateway_addr); 45 | get_config_param_str("custom_dns_ip", &customDNSip); 46 | get_config_param_str("ap_ip", &ap_ip); 47 | get_config_param_str("auth_username", &authUsername); 48 | // get_config_param_str("mac_ap", &macAp); 49 | get_config_param_str("auth_password", &authPass); 50 | 51 | get_config_param_int("web_server", &webServer); 52 | get_config_param_int("led_enable", &ledEnable); 53 | get_config_param_int("custom_dns", &customDnsEnable); 54 | get_config_param_int("dark_mode", &darkModeEnable); 55 | get_config_param_int("randomize_mac", &randomizeMac); 56 | get_config_param_int("max_login_attempts", &max_login_attempts); 57 | get_config_param_int("lock_out_time_minutes", &lock_out_time_minutes); 58 | get_config_param_int("mac_Filter", &isMacFilterEnable); 59 | get_config_param_int("Is_allow_list", &isAllowList); 60 | 61 | ap_ssid = (ap_ssid != NULL) ? ap_ssid : DEFAULT_SSID; 62 | ap_passwd = (ap_passwd != NULL) ? ap_passwd : ""; 63 | ssid = (ssid != NULL) ? ssid : ""; 64 | passwd = (passwd != NULL) ? passwd : ""; 65 | ent_username = (ent_username != NULL) ? ent_username : ""; 66 | ent_identity = (ent_identity != NULL) ? ent_identity : ""; 67 | 68 | static_ip = (static_ip != NULL) ? static_ip : ""; 69 | subnet_mask = (subnet_mask != NULL) ? subnet_mask : ""; 70 | gateway_addr = (gateway_addr != NULL) ? gateway_addr : ""; 71 | 72 | ap_ip = (ap_ip != NULL) ? ap_ip : DEFAULT_AP_IP; 73 | customDNSip = (customDNSip != NULL) ? customDNSip : DEFAULT_DNS1; 74 | 75 | authUsername = (authUsername != NULL) ? authUsername : DEFAULT_ADMIN_USERNAME; 76 | authPass = (authPass != NULL) ? authPass : DEFAULT_ADMIN_PASSWORD; 77 | max_login_attempts = (max_login_attempts <= 0) ? MAX_LOGIN_ATTEMPTS : max_login_attempts; 78 | lock_out_time_minutes = (lock_out_time_minutes <= 0) ? LOCK_OUT_TIME_MINUTES : lock_out_time_minutes; 79 | 80 | IsWebServerEnable = (webServer != 0); 81 | IsLedEnable = (ledEnable != 0); 82 | IsCustomDnsEnable = (customDnsEnable != 0); 83 | IsDarkModeEnable = (darkModeEnable != 0); 84 | IsRandomizeMacEnable = (randomizeMac != 0); 85 | IsMacFilterEnable = (isMacFilterEnable != 0); 86 | IsAllowList = (isAllowList != 0); 87 | auth_info.username = authUsername; 88 | auth_info.password = authPass; 89 | 90 | // if (IsCustomDnsEnable) 91 | // { 92 | // if (strcmp(customDNSip, "default") == 0) 93 | // { 94 | // customDNSip = DEFAULT_DNS1; 95 | // } 96 | // } 97 | 98 | macAp = currentMAC; //(macAp != NULL) ? macAp : currentMAC; 99 | 100 | ESP_LOGI(TAG, "Initialization complected"); 101 | return ESP_OK; 102 | } 103 | -------------------------------------------------------------------------------- /components/utils/mac_filter.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * @license 4 | * this software licensed under MIT 5 | * 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "utils.h" 18 | #include "esp_efuse.h" 19 | #include "esp_system.h" 20 | #include "esp_mac.h" 21 | #include "esp_netif.h" 22 | #include "esp_wifi.h" 23 | 24 | #include "mac_filter.h" 25 | #include "initialization.h" 26 | 27 | 28 | bool check_mac_address_in_nvs(const char *macAddress) { 29 | cJSON *json = retrieve_mac_addresses_as_json(); 30 | if (json == NULL) return false; 31 | 32 | int numMacs = cJSON_GetArraySize(json); 33 | for (int i = 0; i < numMacs; i++) { 34 | if (strcmp(cJSON_GetArrayItem(json, i)->valuestring, macAddress) == 0) { 35 | cJSON_Delete(json); 36 | return true; 37 | } 38 | } 39 | 40 | cJSON_Delete(json); 41 | return false; 42 | } 43 | 44 | //----------------------------------------------------------------------------- 45 | void mac_filter(const uint8_t mac[6], uint8_t aid) { 46 | char mac_address[18]; 47 | sprintf(mac_address, MACSTR, MAC2STR(mac)); 48 | if (IsMacFilterEnable) { 49 | if ((IsAllowList && !check_mac_address_in_nvs(mac_address)) || 50 | (!IsAllowList && check_mac_address_in_nvs(mac_address))) { 51 | ESP_LOGI("MAC Filter","MAC Address %s not allowed, disconnecting...\n", IsAllowList ? "NOT found in Allow List" : "found in Deny List"); 52 | esp_wifi_deauth_sta(aid); 53 | } 54 | } 55 | } 56 | 57 | //----------------------------------------------------------------------------- 58 | esp_err_t check_in_filter_list(const uint8_t mac[6]) { 59 | uint16_t aid; 60 | esp_err_t err = esp_wifi_ap_get_sta_aid(mac, &aid); 61 | if (err == ESP_OK) { 62 | mac_filter(mac, aid); 63 | } else { 64 | printf("Failed to get AID for MAC address\n"); 65 | } 66 | return err; 67 | } 68 | 69 | //----------------------------------------------------------------------------- 70 | void refresh_mac_filter() { 71 | wifi_sta_list_t wifi_sta_list; 72 | // Get the list of connected stations 73 | ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list(&wifi_sta_list)); 74 | 75 | for (int i = 0; i < wifi_sta_list.num; i++) { 76 | wifi_sta_info_t station = wifi_sta_list.sta[i]; 77 | esp_err_t result = check_in_filter_list(station.mac); 78 | if (result != ESP_OK) { 79 | ESP_LOGE("MAC_FILTER", "Error processing MAC address at index %d", i); 80 | } 81 | } 82 | } 83 | 84 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/utils/mac_generator.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "utils.h" 16 | #include "esp_efuse.h" 17 | #include "esp_system.h" 18 | #include "esp_mac.h" 19 | #include "esp_netif.h" 20 | #include "esp_wifi.h" 21 | 22 | #include "router_globals.h" 23 | #include "initialization.h" 24 | #include "mac_generator.h" 25 | 26 | static const char *TAG = "utils/MAC Generator"; 27 | 28 | bool esp_base_mac_addr_check(uint8_t *mac) 29 | { 30 | // Check if MAC address is all zeros or all ones 31 | for (int i = 0; i < 6; i++) 32 | { 33 | if (mac[i] != 0x00 && mac[i] != 0xff) 34 | { 35 | return false; 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | void custom_mac_generator(void) 42 | { 43 | uint8_t default_mac_addr[6] = {0}; 44 | esp_err_t err = esp_efuse_mac_get_default(default_mac_addr); 45 | if (err != ESP_OK) 46 | { 47 | ESP_LOGE(TAG, "Failed to read default MAC address"); 48 | return; 49 | } 50 | if (IsRandomizeMacEnable) 51 | { 52 | uint8_t random_mac_addr[6]; 53 | bool is_valid_mac = false; 54 | memcpy(random_mac_addr, default_mac_addr, sizeof(default_mac_addr)); 55 | while (!is_valid_mac) 56 | { 57 | for (int i = 1; i < 6; i++) 58 | { 59 | random_mac_addr[i] = esp_random() % 256; 60 | } 61 | // Check if the random MAC address is already in use 62 | if (!esp_base_mac_addr_check(random_mac_addr)) 63 | { 64 | is_valid_mac = true; 65 | } 66 | } 67 | // Set the new MAC address with the first byte unchanged 68 | err = esp_base_mac_addr_set(random_mac_addr); 69 | if (err != ESP_OK) 70 | { 71 | ESP_LOGE(TAG, "Failed to set random MAC address"); 72 | return; 73 | } 74 | uint8_t read_mac_addr[6] = {0}; 75 | err = esp_base_mac_addr_get(read_mac_addr); 76 | if (err != ESP_OK) 77 | { 78 | ESP_LOGE(TAG, "Failed to read MAC address"); 79 | return; 80 | } 81 | if (memcmp(read_mac_addr, random_mac_addr, 6) == 0) 82 | { 83 | sprintf(currentMAC, MACSTR, MAC2STR(random_mac_addr)); 84 | } 85 | } 86 | else 87 | { 88 | sprintf(currentMAC, MACSTR, MAC2STR(default_mac_addr)); 89 | } 90 | ESP_LOGI(TAG, "Setting random MAC address: %s", currentMAC); 91 | } 92 | -------------------------------------------------------------------------------- /components/utils/nvm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "esp_err.h" 14 | #include 15 | 16 | #include "nvm.h" 17 | #include "router_globals.h" 18 | #include "utils.h" 19 | #include "mac_filter.h" 20 | 21 | static const char *TAG = "utils/nvm"; 22 | 23 | void initialize_nvs(void) 24 | { 25 | esp_err_t err = nvs_flash_init(); 26 | if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) 27 | { 28 | ESP_ERROR_CHECK(nvs_flash_erase()); 29 | err = nvs_flash_init(); 30 | } 31 | ESP_ERROR_CHECK(err); 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | esp_err_t nvm_set_bool(const char *key, bool value) 36 | { 37 | 38 | nvs_handle handle; 39 | error_t err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &handle); 40 | if (err != ESP_OK) 41 | { 42 | ESP_LOGI(TAG, "NVS open error"); 43 | return ESP_FAIL; 44 | } 45 | return nvs_set_i32(handle, key, bool_to_int(value)); 46 | } 47 | 48 | //----------------------------------------------------------------------------- 49 | esp_err_t nvm_erase(void) 50 | { 51 | nvs_handle_t nvs_handle; 52 | esp_err_t err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs_handle); 53 | if (err != ESP_OK) 54 | { 55 | ESP_LOGI(TAG, "NVS open error"); 56 | return ESP_FAIL; 57 | } 58 | err = nvs_erase_all(nvs_handle); 59 | if (err != ESP_OK) 60 | { 61 | ESP_LOGI(TAG, "NVS Erase all error"); 62 | return ESP_FAIL; 63 | } 64 | err = nvs_commit(nvs_handle); 65 | if (err != ESP_OK) 66 | { 67 | return ESP_FAIL; 68 | } 69 | nvs_close(nvs_handle); 70 | return ESP_OK; 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | cJSON *retrieve_mac_addresses_as_json() { 75 | nvs_handle_t handle; 76 | esp_err_t err = nvs_open(PARAM_NAMESPACE, NVS_READONLY, &handle); 77 | if (err != ESP_OK) { 78 | return NULL; 79 | } 80 | 81 | size_t required_size; 82 | err = nvs_get_str(handle, NVS_KEY, NULL, &required_size); 83 | if (err != ESP_OK || required_size == 0) { 84 | nvs_close(handle); 85 | return NULL; 86 | } 87 | 88 | char *buffer = malloc(required_size); 89 | if (buffer == NULL) { 90 | nvs_close(handle); 91 | return NULL; 92 | } 93 | 94 | err = nvs_get_str(handle, NVS_KEY, buffer, &required_size); 95 | if (err != ESP_OK) { 96 | free(buffer); 97 | nvs_close(handle); 98 | return NULL; 99 | } 100 | 101 | cJSON *json = cJSON_Parse(buffer); 102 | free(buffer); 103 | nvs_close(handle); 104 | return json; 105 | } 106 | 107 | //----------------------------------------------------------------------------- 108 | esp_err_t save_mac_addresses_as_json(cJSON *json) { 109 | nvs_handle_t handle; 110 | esp_err_t err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &handle); 111 | if (err != ESP_OK) { 112 | cJSON_Delete(json); 113 | return err; 114 | } 115 | 116 | char *serialized = cJSON_Print(json); 117 | if (serialized == NULL) { 118 | cJSON_Delete(json); 119 | nvs_close(handle); 120 | return ESP_ERR_NO_MEM; 121 | } 122 | 123 | err = nvs_set_str(handle, NVS_KEY, serialized); 124 | 125 | ESP_LOGI("MACS ARE","%s",serialized); 126 | free(serialized); 127 | nvs_close(handle); 128 | return err; 129 | } 130 | 131 | //----------------------------------------------------------------------------- 132 | esp_err_t store_mac_address_in_nvs(const char *macAddress) { 133 | cJSON *json = retrieve_mac_addresses_as_json(); 134 | if (json == NULL) { 135 | json = cJSON_CreateArray(); 136 | } 137 | 138 | int numMacs = cJSON_GetArraySize(json); 139 | if (numMacs >= MAX_MAC_ADDRESSES) { 140 | cJSON_Delete(json); 141 | return ESP_ERR_NO_MEM; 142 | } 143 | 144 | for (int i = 0; i < numMacs; i++) { 145 | const char *storedMac = cJSON_GetArrayItem(json, i)->valuestring; 146 | if (strcmp(storedMac, macAddress) == 0) { 147 | cJSON_Delete(json); 148 | return ESP_OK; 149 | } 150 | } 151 | 152 | cJSON_AddItemToArray(json, cJSON_CreateString(macAddress)); 153 | 154 | esp_err_t err = save_mac_addresses_as_json(json); 155 | cJSON_Delete(json); 156 | return err; 157 | } 158 | 159 | //----------------------------------------------------------------------------- 160 | esp_err_t remove_mac_address_from_nvs(const char *macAddress) { 161 | cJSON *json = retrieve_mac_addresses_as_json(); 162 | if (json == NULL) { 163 | return ESP_ERR_NVS_NOT_FOUND; 164 | } 165 | 166 | int numMacs = cJSON_GetArraySize(json); 167 | for (int i = 0; i < numMacs; i++) { 168 | const char *storedMac = cJSON_GetArrayItem(json, i)->valuestring; 169 | if (strcmp(storedMac, macAddress) == 0) { 170 | cJSON_DeleteItemFromArray(json, i); 171 | 172 | esp_err_t err = save_mac_addresses_as_json(json); 173 | cJSON_Delete(json); 174 | return err; 175 | } 176 | } 177 | cJSON_Delete(json); 178 | return ESP_ERR_NOT_FOUND; 179 | } 180 | 181 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/utils/utils.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "esp_timer.h" 13 | #include 14 | #include 15 | #include "initialization.h" 16 | #include "router_globals.h" 17 | #include "utils.h" 18 | #include "esp_efuse.h" 19 | #include "esp_system.h" 20 | #include "esp_mac.h" 21 | #include "esp_netif.h" 22 | #include "esp_wifi.h" 23 | 24 | // static const char *TAG = "components/utils"; 25 | 26 | //----------------------------------------------------------------------------- 27 | bool word_check(const char *str, const char *word) 28 | { 29 | return (strstr(str, word) != NULL); 30 | } 31 | 32 | //----------------------------------------------------------------------------- 33 | int bool_to_int(bool value) 34 | { 35 | return value ? 1 : 0; 36 | } 37 | 38 | //----------------------------------------------------------------------------- 39 | bool str_to_bool(const char *key) 40 | { 41 | return strcmp(key, "true") == 0; 42 | } 43 | 44 | //----------------------------------------------------------------------------- 45 | bool is_valid_ip(const char *ip_str) 46 | { 47 | struct in_addr addr; 48 | int result = inet_pton(AF_INET, ip_str, &addr); 49 | return result != 0; 50 | } 51 | 52 | //----------------------------------------------------------------------------- 53 | uint64_t system_uptime_usec(void) 54 | { 55 | return esp_timer_get_time(); 56 | } 57 | 58 | //----------------------------------------------------------------------------- 59 | float system_uptime_s(void) 60 | { 61 | return (float)(system_uptime_usec() / 1000000.0); 62 | } 63 | 64 | //----------------------------------------------------------------------------- 65 | void restart(void) 66 | { 67 | esp_restart(); 68 | } 69 | 70 | //----------------------------------------------------------------------------- 71 | char *param_set_default(const char *def_val) 72 | { 73 | char *retval = malloc(strlen(def_val) + 1); 74 | strcpy(retval, def_val); 75 | return retval; 76 | } 77 | 78 | 79 | char* url_decode(const char* src) { 80 | if (src == NULL) { 81 | return NULL; 82 | } 83 | 84 | char* dest = malloc(strlen(src) + 1); 85 | if (dest == NULL) { 86 | return NULL; 87 | } 88 | 89 | char* p = dest; 90 | while (*src != '\0') { 91 | if (*src == '%' && *(src + 1) != '\0' && *(src + 2) != '\0') { 92 | char hex[3] = { *(src + 1), *(src + 2), '\0' }; 93 | int i = strtol(hex, NULL, 16); 94 | *p++ = (char)i; 95 | src += 3; 96 | } 97 | else { 98 | *p++ = *src++; 99 | } 100 | } 101 | *p = '\0'; 102 | return dest; 103 | } 104 | 105 | //----------------------------------------------------------------------------- 106 | char *html_escape(const char *src) 107 | { 108 | if (src == NULL) 109 | { 110 | return NULL; 111 | } 112 | // Decode the input string using url_decode 113 | char* decoded = url_decode(src); 114 | int len = strlen(decoded); 115 | int esc_len = len + 1; 116 | for (int i = 0; i < len; i++) 117 | { 118 | if (decoded[i] == '\\' || decoded[i] == '\'' || decoded[i] == '\"' || decoded[i] == '&' || decoded[i] == '#' || decoded[i] == ';') 119 | { 120 | esc_len += 5; 121 | } 122 | } 123 | char *res = malloc(sizeof(char) * (esc_len + 1)); 124 | if (res == NULL) 125 | { 126 | return NULL; 127 | } 128 | int j = 0; 129 | for (int i = 0; i < len; i++) 130 | { 131 | if (decoded[i] == '\\' || decoded[i] == '\'' || decoded[i] == '\"' || decoded[i] == '&' || decoded[i] == '#' || decoded[i] == ';') 132 | { 133 | res[j++] = '&'; 134 | res[j++] = 'a'; 135 | res[j++] = 'm'; 136 | res[j++] = 'p'; 137 | res[j++] = ';'; 138 | } 139 | else 140 | { 141 | res[j++] = decoded[i]; 142 | } 143 | } 144 | res[j] = '\0'; 145 | free(decoded); 146 | return res; 147 | } -------------------------------------------------------------------------------- /components/web_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS request_handler.c response_handler.c get_data_handler.c 2 | storage_handler.c auth_handler.c web_server.c 3 | 4 | INCLUDE_DIRS include 5 | REQUIRES esp_http_server esp-tls json esp_http_client utils 6 | cmd_router cmd_nvs ota_handler wifi_handler) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /components/web_server/auth_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include "stdio.h" 12 | #include "string.h" 13 | #include "request_handler.h" 14 | #include "auth_handler.h" 15 | #include "nvs.h" 16 | #include "cmd_nvs.h" 17 | #include "router_globals.h" 18 | #include "ota_handler.h" 19 | #include "esp_http_server.h" 20 | #include "utils.h" 21 | 22 | char auth_buffer[512]; 23 | #define HTTPD_401 "401 Unauthorized" 24 | #define HTTPD_403 "403 Forbidden" 25 | 26 | int max_login_attempts; 27 | int lock_out_time_minutes; 28 | 29 | int failed_attempts_count = 0; 30 | float block_start_time = 0; 31 | 32 | #define BLOCKING_TIME_SEC (lock_out_time_minutes * 60) // convert minutes to seconds 33 | 34 | basic_auth_info_t auth_info = { 35 | 36 | }; 37 | 38 | //----------------------------------------------------------------------------- 39 | // block's the user for some time to avoid brute force attacks 40 | static bool block_request(void) 41 | { 42 | float current_time = system_uptime_s(); 43 | // Reset block_start_time if blocking period has passed 44 | if ((block_start_time > 0) && ((block_start_time + BLOCKING_TIME_SEC) < current_time)) 45 | { 46 | block_start_time = 0; 47 | failed_attempts_count = 0; 48 | } 49 | // printf("current time : %f, blocked time: %f\n", current_time, block_start_time); 50 | return (failed_attempts_count >= max_login_attempts && (block_start_time + BLOCKING_TIME_SEC) > current_time); 51 | } 52 | 53 | //----------------------------------------------------------------------------- 54 | static char *http_auth_basic(char *username, char *password) 55 | { 56 | int out; 57 | char user_info[128]; 58 | static char digest[512]; 59 | size_t n = 0; 60 | sprintf(user_info, "%s:%s", username, password); 61 | 62 | esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info)); 63 | 64 | // 6: The length of the "Basic " string 65 | // n: Number of bytes for a base64 encode format 66 | // 1: Number of bytes for a reserved which be used to fill zero 67 | if (sizeof(digest) > (6 + n + 1)) 68 | { 69 | strcpy(digest, "Basic "); 70 | esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info)); 71 | } 72 | 73 | return digest; 74 | } 75 | 76 | //----------------------------------------------------------------------------- 77 | esp_err_t authentication_handler(httpd_req_t *req, int req_id) 78 | { 79 | 80 | if (block_request()) 81 | { 82 | httpd_resp_set_status(req, HTTPD_403); 83 | httpd_resp_set_hdr(req, "Connection", "close"); 84 | httpd_resp_send(req, NULL, 0); 85 | return ESP_OK; 86 | } 87 | 88 | size_t buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1; 89 | if ((buf_len > 1) && (buf_len <= sizeof(auth_buffer))) 90 | { 91 | char auth_buffer[buf_len]; 92 | if (httpd_req_get_hdr_value_str(req, "Authorization", auth_buffer, buf_len) == ESP_OK) 93 | { 94 | char *auth_credentials = http_auth_basic(auth_info.username, auth_info.password); 95 | if (!strncmp(auth_credentials, auth_buffer, buf_len)) 96 | { 97 | failed_attempts_count = 0; 98 | httpd_resp_set_status(req, HTTPD_200); 99 | 100 | switch (req_id) 101 | { 102 | case POST_OTA_UPDATE_ID: 103 | return ota_update_handler(req); 104 | case COMMON_JS_ID: 105 | return common_js_response(req); 106 | case COMMON_DATA_ID: 107 | return common_data_request(req); 108 | default: 109 | return common_page_response_handler(req, req_id); 110 | } 111 | } 112 | } 113 | } 114 | printf("Not authenticated\n"); 115 | failed_attempts_count++; 116 | if (failed_attempts_count >= max_login_attempts && block_start_time == 0) 117 | { 118 | block_start_time = system_uptime_s(); 119 | } 120 | 121 | httpd_resp_set_status(req, HTTPD_401); 122 | httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"Hello authenticate first\""); 123 | httpd_resp_send(req, NULL, 0); 124 | 125 | return ESP_OK; 126 | } 127 | -------------------------------------------------------------------------------- /components/web_server/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/web_server/get_data_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "get_data_handler.h" 14 | #include "router_globals.h" 15 | #include "request_handler.h" 16 | #include "auth_handler.h" 17 | #include "initialization.h" 18 | #include "wifi_handler.h" 19 | 20 | #define MAX_CLIENTS 10 21 | 22 | static const char *TAG = "Web server/get_data_handler"; 23 | 24 | //----------------------------------------------------------------------------- 25 | esp_err_t get_settings_data_handler(httpd_req_t *req) 26 | { 27 | parms_init(); 28 | 29 | cJSON *root; 30 | root = cJSON_CreateObject(); 31 | 32 | cJSON_AddStringToObject(root, "ssid", ssid); 33 | cJSON_AddStringToObject(root, "password", passwd); 34 | cJSON_AddStringToObject(root, "entUsername", ent_username); 35 | cJSON_AddStringToObject(root, "entIdentity", ent_identity); 36 | cJSON_AddStringToObject(root, "apSsid", ap_ssid); 37 | cJSON_AddStringToObject(root, "apPassword", ap_passwd); 38 | 39 | cJSON_AddStringToObject(root, "apIP", ap_ip); 40 | 41 | cJSON_AddStringToObject(root, "staticIP", static_ip); 42 | cJSON_AddStringToObject(root, "subnetMask", subnet_mask); 43 | cJSON_AddStringToObject(root, "gateWay", gateway_addr); 44 | 45 | cJSON_AddStringToObject(root, "macAp", currentMAC); 46 | cJSON_AddStringToObject(root, "dnsIP", customDNSip); 47 | 48 | cJSON_AddBoolToObject(root, "randMacAp", IsRandomizeMacEnable); 49 | cJSON_AddBoolToObject(root, "CustomDns", IsCustomDnsEnable); 50 | cJSON_AddBoolToObject(root, "ledEnable", IsLedEnable); 51 | cJSON_AddBoolToObject(root, "darkMode", IsDarkModeEnable); 52 | cJSON_AddBoolToObject(root, "webServer", IsWebServerEnable); 53 | cJSON_AddBoolToObject(root, "macFilterEnabled", IsMacFilterEnable); 54 | cJSON_AddStringToObject(root, "authUsername", authUsername); 55 | cJSON_AddStringToObject(root, "authPassword", authPass); 56 | cJSON_AddNumberToObject(root, "maxLoginAttempts", max_login_attempts); 57 | cJSON_AddNumberToObject(root, "blockingTimeMin", lock_out_time_minutes); 58 | char *my_json_string = cJSON_Print(root); 59 | 60 | httpd_resp_set_type(req, "application/json"); 61 | httpd_resp_send(req, my_json_string, HTTPD_RESP_USE_STRLEN); 62 | // ESP_LOGI(TAG, "Requesting settings Data"); 63 | 64 | cJSON_Delete(root); 65 | return ESP_OK; 66 | } 67 | 68 | //----------------------------------------------------------------------------- 69 | esp_err_t get_scan_data_handler(httpd_req_t *req) 70 | { 71 | 72 | char *scan_result = wifi_scan_handler(); 73 | 74 | httpd_resp_set_type(req, "application/json"); 75 | 76 | httpd_resp_send(req, scan_result, HTTPD_RESP_USE_STRLEN); 77 | ESP_LOGI(TAG, "Requesting scan aps Data"); 78 | free(scan_result); 79 | 80 | return ESP_OK; 81 | } 82 | 83 | //----------------------------------------------------------------------------- 84 | esp_err_t get_info_data_handler(httpd_req_t *req) 85 | { 86 | char *info_result = wifi_info_handler(); 87 | 88 | httpd_resp_set_type(req, "application/json"); 89 | httpd_resp_send(req, info_result, HTTPD_RESP_USE_STRLEN); 90 | // ESP_LOGI(TAG, "Requesting Info Data"); 91 | free(info_result); 92 | return ESP_OK; 93 | } 94 | -------------------------------------------------------------------------------- /components/web_server/include/auth_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | #include "initialization.h" 18 | 19 | esp_err_t authentication_handler(httpd_req_t *req, int url_id_number); 20 | extern int max_login_attempts; 21 | extern int lock_out_time_minutes; 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /components/web_server/include/get_data_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #pragma once 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /components/web_server/include/request_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #ifndef URL_REQUEST_HANDLER 10 | #define URL_REQUEST_HANDLER 11 | 12 | #endif 13 | 14 | #pragma once 15 | 16 | #ifdef __cplusplus 17 | extern "C" 18 | { 19 | #endif 20 | 21 | #include "esp_http_server.h" 22 | 23 | #define SCAN_PAGE_ID 1 24 | #define SETTINGS_PAGE_ID 2 25 | #define INFO_PAGE_ID 3 26 | #define MAIN_CSS_ID 4 27 | #define DARK_CSS_ID 5 28 | 29 | #define COMMON_JS_ID 6 30 | #define COMMON_DATA_ID 7 31 | 32 | #define POST_OTA_UPDATE_ID 8 33 | #define ERR_404_PAGE_ID 404 34 | 35 | // Register NVS functions 36 | esp_err_t get_scan_handler(httpd_req_t *req); 37 | esp_err_t get_settings_handler(httpd_req_t *req); 38 | 39 | esp_err_t get_info_handler(httpd_req_t *req); 40 | esp_err_t get_main_css_handler(httpd_req_t *req); 41 | esp_err_t get_dark_css_handler(httpd_req_t *req); 42 | esp_err_t get_error_404_handler(httpd_req_t *req); 43 | 44 | esp_err_t common_page_response_handler(httpd_req_t *req, int req_id); 45 | esp_err_t common_js_path_handler(httpd_req_t *req); 46 | esp_err_t common_js_response(httpd_req_t *req); 47 | 48 | esp_err_t common_data_handler(httpd_req_t *req); 49 | esp_err_t common_data_request(httpd_req_t *req); 50 | 51 | esp_err_t http_404_error_handler(httpd_req_t *req); 52 | 53 | esp_err_t get_settings_data_handler(httpd_req_t *req); 54 | esp_err_t save_settings_data_handler(httpd_req_t *req); 55 | 56 | esp_err_t get_scan_data_handler(httpd_req_t *req); 57 | esp_err_t get_info_data_handler(httpd_req_t *req); 58 | 59 | // esp_err_t ssids_post_handler(httpd_req_t *req); 60 | esp_err_t post_ota_update_handler(httpd_req_t *req); 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /components/web_server/include/storage_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include "esp_err.h" 17 | #include 18 | 19 | esp_err_t save_settings_data_handler(httpd_req_t *req); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /components/web_server/include/web_server.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | #include 16 | 17 | void stop_web_server(void); 18 | void toggle_webserver(void); 19 | extern httpd_handle_t server; 20 | httpd_handle_t start_webserver(void); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /components/web_server/request_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include "esp_http_server.h" 11 | #include "auth_handler.h" 12 | #include "request_handler.h" 13 | // static const char *TAG = "components/uurl_request_handler"; 14 | #include 15 | 16 | /* ******** Request Handlers ************ */ 17 | 18 | //----------------------------------------------------------------------------- 19 | esp_err_t get_scan_handler(httpd_req_t *req) 20 | { 21 | return authentication_handler(req, SCAN_PAGE_ID); 22 | } 23 | 24 | 25 | esp_err_t get_settings_handler(httpd_req_t *req) 26 | { 27 | return authentication_handler(req, SETTINGS_PAGE_ID); 28 | } 29 | 30 | 31 | esp_err_t get_info_handler(httpd_req_t *req) 32 | { 33 | return authentication_handler(req, INFO_PAGE_ID); 34 | } 35 | 36 | 37 | //----------------------------------------------------------------------------- 38 | esp_err_t get_main_css_handler(httpd_req_t *req) 39 | { 40 | return common_page_response_handler(req, MAIN_CSS_ID); 41 | } 42 | esp_err_t get_dark_css_handler(httpd_req_t *req) 43 | { 44 | return common_page_response_handler(req, DARK_CSS_ID); 45 | } 46 | 47 | 48 | //----------------------------------------------------------------------------- 49 | esp_err_t common_js_path_handler(httpd_req_t *req) 50 | { 51 | if (strcmp(req->uri, "/js/functions.js") == 0) 52 | { 53 | return common_js_response(req); 54 | } 55 | return authentication_handler(req, COMMON_JS_ID); 56 | } 57 | 58 | 59 | //----------------------------------------------------------------------------- 60 | esp_err_t common_data_handler(httpd_req_t *req) 61 | { 62 | return authentication_handler(req, COMMON_DATA_ID); 63 | } 64 | 65 | 66 | //----------------------------------------------------------------------------- 67 | esp_err_t post_ota_update_handler(httpd_req_t *req) 68 | { 69 | return authentication_handler(req, POST_OTA_UPDATE_ID); 70 | } 71 | 72 | 73 | //----------------------------------------------------------------------------- 74 | esp_err_t get_error_404_handler(httpd_req_t *req) 75 | { 76 | return common_page_response_handler(req, ERR_404_PAGE_ID); 77 | } -------------------------------------------------------------------------------- /components/web_server/response_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include "esp_http_server.h" 11 | #include 12 | #include "router_globals.h" 13 | #include "request_handler.h" 14 | #include "initialization.h" 15 | #include "storage_handler.h" 16 | #include "utils.h" 17 | #include "nvm.h" 18 | static const char *TAG = "components/url_requesr_handler"; 19 | 20 | // it is possible to simplify the code by using a macro that specifies the symbol name. 21 | 22 | // <<<--- it is equivalent to --->>> 23 | // extern const char name_start[] asm("_binary_name_start"); 24 | // extern const char name_end[] asm("_binary_name_end"); 25 | // #define HTTPD_404 404_html_gz_start 26 | 27 | #define BINARY_SYMBOL(name) \ 28 | extern const char name##_start[] asm("_binary_" #name "_start"); \ 29 | extern const char name##_end[] asm("_binary_" #name "_end"); 30 | 31 | BINARY_SYMBOL(index_html_gz); 32 | BINARY_SYMBOL(settings_html_gz); 33 | BINARY_SYMBOL(info_html_gz); 34 | BINARY_SYMBOL(error_404_html_gz); 35 | 36 | BINARY_SYMBOL(main_css_gz); 37 | BINARY_SYMBOL(dark_css_gz); 38 | 39 | // javaScript 40 | BINARY_SYMBOL(scan_js_gz); 41 | BINARY_SYMBOL(info_js_gz); 42 | BINARY_SYMBOL(settings_js_gz); 43 | BINARY_SYMBOL(functions_js_gz); 44 | BINARY_SYMBOL(info_js_gz); 45 | 46 | /************* returns subsequent Response pages***************/ 47 | typedef struct 48 | { 49 | const int id; 50 | const char *type; 51 | const uint8_t *start; 52 | const uint8_t *end; 53 | } url_handler_t; 54 | 55 | url_handler_t handlers[] = { 56 | {SCAN_PAGE_ID, "text/html", (const uint8_t *)index_html_gz_start, (const uint8_t *)index_html_gz_end}, 57 | {SETTINGS_PAGE_ID, "text/html", (const uint8_t *)settings_html_gz_start, (const uint8_t *)settings_html_gz_end}, 58 | {INFO_PAGE_ID, "text/html", (const uint8_t *)info_html_gz_start, (const uint8_t *)info_html_gz_end}, 59 | {ERR_404_PAGE_ID, "text/html", (const uint8_t *)error_404_html_gz_start, (const uint8_t *)error_404_html_gz_end}, 60 | {MAIN_CSS_ID, "text/css", (const uint8_t *)main_css_gz_start, (const uint8_t *)main_css_gz_end}, 61 | {DARK_CSS_ID, "text/css", (const uint8_t *)dark_css_gz_start, (const uint8_t *)dark_css_gz_end}, 62 | }; 63 | 64 | 65 | //----------------------------------------------------------------------------- 66 | esp_err_t ICACHE_FLASH_ATTR common_page_response_handler(httpd_req_t *req, int req_id) 67 | { 68 | for (int i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) 69 | { 70 | if (handlers[i].id == req_id) 71 | { 72 | if (!IsDarkModeEnable && req_id == DARK_CSS_ID) 73 | { 74 | httpd_resp_set_type(req, handlers[i].type); 75 | return httpd_resp_send(req, "/* Dark mode disabled */", HTTPD_RESP_USE_STRLEN); 76 | } 77 | if (req_id == ERR_404_PAGE_ID) 78 | { 79 | httpd_resp_set_status(req, HTTPD_404); 80 | } 81 | 82 | const size_t handler_size = handlers[i].end - handlers[i].start; 83 | httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); 84 | return httpd_resp_send(req, (const char *)handlers[i].start, handler_size); 85 | } 86 | } 87 | httpd_resp_send_404(req); 88 | return ESP_OK; 89 | } 90 | 91 | /////////////////////////////////////////**************JAVA_SCRIPT*********/////////////////////////////////////////////////////// 92 | 93 | typedef struct 94 | { 95 | const char *uri; 96 | const uint8_t *start; 97 | const uint8_t *end; 98 | } js_file_t; 99 | 100 | js_file_t js_files[] = { 101 | {"/js/scan.js", (const uint8_t *)scan_js_gz_start, (const uint8_t *)scan_js_gz_end}, 102 | {"/js/info.js", (const uint8_t *)info_js_gz_start, (const uint8_t *)info_js_gz_end}, 103 | {"/js/settings.js", (const uint8_t *)settings_js_gz_start, (const uint8_t *)settings_js_gz_end}, 104 | {"/js/functions.js", (const uint8_t *)functions_js_gz_start, (const uint8_t *)functions_js_gz_end}, 105 | }; 106 | const size_t num_js_files = sizeof(js_files) / sizeof(js_files[0]); 107 | 108 | 109 | //----------------------------------------------------------------------------- 110 | esp_err_t ICACHE_FLASH_ATTR common_js_response(httpd_req_t *req) 111 | { 112 | for (int i = 0; i < num_js_files; i++) 113 | { 114 | if (strcmp(req->uri, js_files[i].uri) == 0) 115 | { 116 | const size_t js_size = (js_files[i].end - js_files[i].start); 117 | httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); 118 | httpd_resp_set_type(req, "application/javascript"); 119 | 120 | return httpd_resp_send(req, (const char *)js_files[i].start, js_size); 121 | } 122 | } 123 | return http_404_error_handler(req); 124 | } 125 | 126 | //----------------------------------------------------------------------------- 127 | esp_err_t common_data_request(httpd_req_t *req) 128 | { 129 | 130 | if (strcmp(req->uri, "/data/settings.json") == 0) 131 | { 132 | return get_settings_data_handler(req); 133 | } 134 | else if (word_check(req->uri, "/data/settingsSave.json")) 135 | { 136 | return save_settings_data_handler(req); 137 | } 138 | 139 | else if (strcmp(req->uri, "/data/APScanResults.json") == 0) 140 | { 141 | return get_scan_data_handler(req); 142 | } 143 | else if (strcmp(req->uri, "/data/sysinfo.json") == 0) 144 | { 145 | return get_info_data_handler(req); 146 | } 147 | else if (strcmp(req->uri, "/data/ClientScanTime.json") == 0) 148 | { 149 | httpd_resp_set_type(req, "application/json"); 150 | return httpd_resp_send(req, "true", HTTPD_RESP_USE_STRLEN); 151 | } 152 | else if (strcmp(req->uri, "/data/restart.json") == 0) 153 | { 154 | ESP_LOGW(TAG, "Restarting now..."); 155 | httpd_resp_set_type(req, "application/json"); 156 | if (httpd_resp_send(req, "true", HTTPD_RESP_USE_STRLEN) == ESP_OK) 157 | { 158 | vTaskDelay(pdMS_TO_TICKS(2000)); 159 | restart(); 160 | } 161 | return ESP_OK; 162 | } 163 | else if (strcmp(req->uri, "/data/settingsReset.json") == 0) 164 | { 165 | 166 | if (nvm_erase() == ESP_OK) 167 | httpd_resp_send(req, "true", HTTPD_RESP_USE_STRLEN); 168 | 169 | else 170 | return httpd_resp_send(req, "false", HTTPD_RESP_USE_STRLEN); 171 | } 172 | 173 | return http_404_error_handler(req); 174 | } 175 | -------------------------------------------------------------------------------- /components/web_server/storage_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "cmd_nvs.h" 13 | #include "auth_handler.h" 14 | #include "router_globals.h" 15 | #include "request_handler.h" 16 | #include "initialization.h" 17 | #include "utils.h" 18 | #include "mac_filter.h" 19 | 20 | static const char *TAG = "url_handler/get_data_handler"; 21 | 22 | bool error = false; 23 | 24 | //----------------------------------------------------------------------------- 25 | esp_err_t save_settings_data_handler(httpd_req_t *req) 26 | { 27 | 28 | char *query_buf; 29 | size_t query_buf_len; 30 | char param_buf[64]; 31 | esp_err_t err; 32 | bool valid_query = false; 33 | 34 | // get the query string 35 | query_buf_len = httpd_req_get_url_query_len(req) + 1; 36 | query_buf = malloc(query_buf_len); 37 | if (query_buf == NULL) 38 | { 39 | return ESP_ERR_NO_MEM; 40 | } 41 | err = httpd_req_get_url_query_str(req, query_buf, query_buf_len); 42 | if (err != ESP_OK) 43 | { 44 | free(query_buf); 45 | return err; 46 | } 47 | 48 | nvs_handle handle; 49 | err = nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &handle); 50 | if (err != ESP_OK) 51 | { 52 | ESP_LOGI(TAG, "NVS error"); 53 | valid_query = false; 54 | } 55 | 56 | if (httpd_query_key_value(query_buf, "ssid", param_buf, sizeof(param_buf)) == ESP_OK) 57 | { 58 | ssid = html_escape(strdup(param_buf)); 59 | err = nvs_set_str(handle, "ssid", ssid); 60 | } 61 | if (httpd_query_key_value(query_buf, "password", param_buf, sizeof(param_buf)) == ESP_OK) 62 | { 63 | passwd = html_escape(strdup(param_buf)); 64 | err = nvs_set_str(handle, "passwd", passwd); 65 | } 66 | if (httpd_query_key_value(query_buf, "ent_username", param_buf, sizeof(param_buf)) == ESP_OK) 67 | { 68 | ent_username = html_escape(strdup(param_buf)); 69 | err = nvs_set_str(handle, "ent_username", ent_username); 70 | } 71 | if (httpd_query_key_value(query_buf, "ent_identity", param_buf, sizeof(param_buf)) == ESP_OK) 72 | { 73 | ent_identity = html_escape(strdup(param_buf)); 74 | err = nvs_set_str(handle, "ent_identity", ent_identity); 75 | } 76 | if (httpd_query_key_value(query_buf, "apSsid", param_buf, sizeof(param_buf)) == ESP_OK) 77 | { 78 | ap_ssid = html_escape(strdup(param_buf)); 79 | err = nvs_set_str(handle, "ap_ssid", ap_ssid); 80 | } 81 | 82 | if (httpd_query_key_value(query_buf, "apPassword", param_buf, sizeof(param_buf)) == ESP_OK) 83 | { 84 | ap_passwd = html_escape(strdup(param_buf)); 85 | err = nvs_set_str(handle, "ap_passwd", ap_passwd); 86 | } 87 | // if (httpd_query_key_value(query_buf, "macAp", param_buf, sizeof(param_buf)) == ESP_OK) 88 | // { 89 | // macAp = html_escape(strdup(param_buf)); 90 | // err = nvs_set_str(handle, "macAp", macAp); 91 | // } 92 | if (httpd_query_key_value(query_buf, "staticIP", param_buf, sizeof(param_buf)) == ESP_OK) 93 | { 94 | static_ip = html_escape(strdup(param_buf)); 95 | if (is_valid_ip(static_ip)) 96 | err = nvs_set_str(handle, "static_ip", static_ip); 97 | else 98 | ESP_LOGE(TAG, "Invalid Static IP Address"); 99 | } 100 | if (httpd_query_key_value(query_buf, "subnetMask", param_buf, sizeof(param_buf)) == ESP_OK) 101 | { 102 | subnet_mask = html_escape(strdup(param_buf)); 103 | if (is_valid_ip(subnet_mask)) 104 | err = nvs_set_str(handle, "subnet_mask", subnet_mask); 105 | else 106 | ESP_LOGE(TAG, "Invalid Subnet Mask Address"); 107 | } 108 | if (httpd_query_key_value(query_buf, "gateWay", param_buf, sizeof(param_buf)) == ESP_OK) 109 | { 110 | gateway_addr = html_escape(strdup(param_buf)); 111 | if (is_valid_ip(gateway_addr)) 112 | err = nvs_set_str(handle, "gateway_addr", gateway_addr); 113 | else 114 | ESP_LOGE(TAG, "Invalid Gateway Addr Address"); 115 | } 116 | if (httpd_query_key_value(query_buf, "apIP", param_buf, sizeof(param_buf)) == ESP_OK) 117 | { 118 | ap_ip = html_escape(strdup(param_buf)); 119 | if (is_valid_ip(ap_ip)) 120 | err = nvs_set_str(handle, "ap_ip", ap_ip); 121 | else 122 | ESP_LOGE(TAG, "Invalid AP IP Address"); 123 | } 124 | 125 | if (httpd_query_key_value(query_buf, "dnsIP", param_buf, sizeof(param_buf)) == ESP_OK) 126 | { 127 | customDNSip = html_escape(strdup(param_buf)); 128 | if (strcmp(customDNSip, "default") != 0) 129 | { 130 | if (is_valid_ip(customDNSip)) 131 | err = nvs_set_str(handle, "custom_dns_ip", customDNSip); 132 | else 133 | ESP_LOGE(TAG, "Invalid DNS IP Address"); 134 | } 135 | } 136 | 137 | if (httpd_query_key_value(query_buf, "adminUsername", param_buf, sizeof(param_buf)) == ESP_OK) 138 | { 139 | authUsername = html_escape(strdup(param_buf)); 140 | err = nvs_set_str(handle, "auth_username", authUsername); 141 | } 142 | if (httpd_query_key_value(query_buf, "adminPassword", param_buf, sizeof(param_buf)) == ESP_OK) 143 | { 144 | authPass = html_escape(strdup(param_buf)); 145 | err = nvs_set_str(handle, "auth_password", authPass); 146 | } 147 | 148 | if (httpd_query_key_value(query_buf, "maxLoginAttempts", param_buf, sizeof(param_buf)) == ESP_OK) 149 | { 150 | max_login_attempts = atoi(html_escape(strdup(param_buf))); 151 | err = nvs_set_i32(handle, "max_login_attempts", max_login_attempts); 152 | } 153 | 154 | if (httpd_query_key_value(query_buf, "blockingTimeMin", param_buf, sizeof(param_buf)) == ESP_OK) 155 | { 156 | lock_out_time_minutes = atoi(html_escape(strdup(param_buf))); 157 | err = nvs_set_i32(handle, "lock_out_time_minutes", lock_out_time_minutes); 158 | } 159 | 160 | if (httpd_query_key_value(query_buf, "randMacAp", param_buf, sizeof(param_buf)) == ESP_OK) 161 | { 162 | IsRandomizeMacEnable = str_to_bool(html_escape(strdup(param_buf))); 163 | err = nvs_set_i32(handle, "randomize_mac", bool_to_int(IsRandomizeMacEnable)); 164 | } 165 | 166 | if (httpd_query_key_value(query_buf, "CustomDns", param_buf, sizeof(param_buf)) == ESP_OK) 167 | { 168 | 169 | IsCustomDnsEnable = str_to_bool(html_escape(strdup(param_buf))); 170 | err = nvs_set_i32(handle, "custom_dns", bool_to_int(IsCustomDnsEnable)); 171 | } 172 | 173 | if (httpd_query_key_value(query_buf, "ledEnable", param_buf, sizeof(param_buf)) == ESP_OK) 174 | { 175 | IsLedEnable = str_to_bool(html_escape(strdup(param_buf))); 176 | err = nvs_set_i32(handle, "led_enable", bool_to_int(IsLedEnable)); 177 | } 178 | 179 | if (httpd_query_key_value(query_buf, "webServer", param_buf, sizeof(param_buf)) == ESP_OK) 180 | { 181 | IsWebServerEnable = str_to_bool(html_escape(strdup(param_buf))); 182 | err = nvs_set_i32(handle, "web_server", bool_to_int(IsWebServerEnable)); 183 | } 184 | if (httpd_query_key_value(query_buf, "darkMode", param_buf, sizeof(param_buf)) == ESP_OK) 185 | { 186 | IsDarkModeEnable = str_to_bool(html_escape(strdup(param_buf))); 187 | err = nvs_set_i32(handle, "dark_mode", bool_to_int(IsDarkModeEnable)); 188 | } 189 | if (httpd_query_key_value(query_buf, "macFilterEnable", param_buf, sizeof(param_buf)) == ESP_OK) 190 | { 191 | IsMacFilterEnable = str_to_bool(html_escape(strdup(param_buf))); 192 | err = nvs_set_i32(handle, "mac_Filter", bool_to_int(IsMacFilterEnable)); 193 | printf("Commit failed! (%s)\n", esp_err_to_name(err)); 194 | 195 | } 196 | /////////////////// 197 | if (httpd_query_key_value(query_buf, "add_mac_address", param_buf, sizeof(param_buf)) == ESP_OK) 198 | { 199 | store_mac_address_in_nvs(html_escape(strdup(param_buf))); 200 | refresh_mac_filter(); 201 | 202 | } 203 | 204 | if (httpd_query_key_value(query_buf, "remove_mac_address", param_buf, sizeof(param_buf)) == ESP_OK) 205 | { 206 | remove_mac_address_from_nvs(html_escape(strdup(param_buf))); 207 | refresh_mac_filter(); 208 | } 209 | 210 | if (httpd_query_key_value(query_buf, "filter_list_type", param_buf, sizeof(param_buf)) == ESP_OK) 211 | { 212 | IsAllowList = word_check(html_escape(strdup(param_buf)),"Allow"); 213 | err = nvs_set_i32(handle, "Is_allow_list", bool_to_int(IsAllowList)); 214 | refresh_mac_filter(); 215 | } 216 | 217 | 218 | ///////////////// 219 | 220 | valid_query = (err == ESP_OK); 221 | 222 | if (valid_query) 223 | { 224 | err = nvs_commit(handle); 225 | if (err != ESP_OK) 226 | { 227 | free(query_buf); 228 | nvs_close(handle); 229 | return err; 230 | } 231 | nvs_close(handle); 232 | free(query_buf); 233 | return httpd_resp_send(req, "true", HTTPD_RESP_USE_STRLEN); 234 | } 235 | else 236 | { 237 | nvs_close(handle); 238 | free(query_buf); 239 | return httpd_resp_send(req, "false", HTTPD_RESP_USE_STRLEN); 240 | } 241 | } 242 | 243 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/web_server/web_server.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | /* Simple HTTP Server Example 9 | 10 | This example code is in the Public Domain (or CC0 licensed, at your option.) 11 | 12 | Unless required by applicable law or agreed to in writing, this 13 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 | CONDITIONS OF ANY KIND, either express or implied. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "web_server.h" 20 | #include "router_globals.h" 21 | #include "request_handler.h" 22 | #include "auth_handler.h" 23 | 24 | static const char *TAG = "HTTPServer"; 25 | 26 | // declare global variable to store server handle 27 | httpd_handle_t server = NULL; 28 | 29 | static httpd_uri_t get_scan = { 30 | .uri = "/", 31 | .method = HTTP_GET, 32 | .handler = get_scan_handler, 33 | .user_ctx = &auth_info}; 34 | 35 | static httpd_uri_t get_settings = { 36 | .uri = "/settings", 37 | .method = HTTP_GET, 38 | .handler = get_settings_handler, 39 | .user_ctx = &auth_info}; 40 | 41 | static httpd_uri_t get_info = { 42 | .uri = "/info", 43 | .method = HTTP_GET, 44 | .handler = get_info_handler, 45 | .user_ctx = &auth_info}; 46 | 47 | static httpd_uri_t get_main_css = { 48 | .uri = "/main.css", 49 | .method = HTTP_GET, 50 | .handler = get_main_css_handler, 51 | .user_ctx = &auth_info}; 52 | 53 | static httpd_uri_t get_dark_css = { 54 | .uri = "/dark.css", 55 | .method = HTTP_GET, 56 | .handler = get_dark_css_handler, 57 | .user_ctx = &auth_info}; 58 | 59 | static httpd_uri_t get_error_404 = { 60 | .uri = "/404", 61 | .method = HTTP_GET, 62 | .handler = get_error_404_handler, 63 | .user_ctx = &auth_info}; 64 | 65 | // javascript 66 | static httpd_uri_t common_js_path = { 67 | .uri = "/js/*", 68 | .method = HTTP_GET, 69 | .handler = common_js_path_handler, 70 | .user_ctx = &auth_info}; 71 | 72 | static httpd_uri_t common_data_path = { 73 | .uri = "/data/*", 74 | .method = HTTP_GET, 75 | .handler = common_data_handler, 76 | .user_ctx = &auth_info}; 77 | 78 | static httpd_uri_t settings_post_path = { 79 | .uri = "/data/settingsSave.json", 80 | .method = HTTP_POST, 81 | .handler = common_data_handler, 82 | .user_ctx = &auth_info}; 83 | 84 | static httpd_uri_t post_ota_update = { 85 | .uri = "/ota", 86 | .method = HTTP_POST, 87 | .handler = post_ota_update_handler, 88 | .user_ctx = &auth_info}; 89 | 90 | //----------------------------------------------------------------------------- 91 | esp_err_t http_404_error_handler(httpd_req_t *req) 92 | { 93 | httpd_resp_set_status(req, "302 Temporary Redirect"); 94 | httpd_resp_set_hdr(req, "Location", "/404"); 95 | httpd_resp_send(req, "Redirect to 404 page", HTTPD_RESP_USE_STRLEN); 96 | return ESP_OK; 97 | } 98 | 99 | static httpd_uri_t error_404_handler = { 100 | .uri = "/*", 101 | .method = HTTP_GET, 102 | .handler = http_404_error_handler, 103 | .user_ctx = NULL}; 104 | 105 | //----------------------------------------------------------------------------- 106 | httpd_handle_t start_webserver(void) 107 | { 108 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 109 | // config.task_priority = 6, 110 | // config.max_resp_headers = 1024; 111 | config.max_uri_handlers = 15; 112 | config.uri_match_fn = httpd_uri_match_wildcard; 113 | config.lru_purge_enable = true; 114 | 115 | // config.stack_size = 10000; 116 | 117 | if (httpd_start(&server, &config) != ESP_OK) 118 | { 119 | ESP_LOGE(TAG, "Error starting server!"); 120 | return NULL; 121 | } 122 | ESP_LOGI(TAG, "Registering URI handlers"); 123 | httpd_register_uri_handler(server, &get_scan); 124 | httpd_register_uri_handler(server, &get_info); 125 | httpd_register_uri_handler(server, &get_settings); 126 | httpd_register_uri_handler(server, &get_main_css); 127 | httpd_register_uri_handler(server, &get_dark_css); 128 | httpd_register_uri_handler(server, &common_js_path); 129 | httpd_register_uri_handler(server, &common_data_path); 130 | httpd_register_uri_handler(server, &settings_post_path); 131 | httpd_register_uri_handler(server, &post_ota_update); 132 | httpd_register_uri_handler(server, &get_error_404); 133 | httpd_register_uri_handler(server, &error_404_handler); 134 | return server; 135 | } 136 | 137 | //----------------------------------------------------------------------------- 138 | void stop_web_server(void) 139 | { 140 | if (server != NULL) 141 | { 142 | httpd_stop(server); 143 | server = NULL; 144 | ESP_LOGI(TAG, "Http server stoped"); 145 | } 146 | } 147 | 148 | //----------------------------------------------------------------------------- 149 | void toggle_webserver(void) 150 | { 151 | IsWebServerEnable = !IsWebServerEnable; 152 | if (IsWebServerEnable) 153 | { 154 | server = start_webserver(); 155 | } 156 | else 157 | { 158 | stop_web_server(); 159 | } 160 | } 161 | 162 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/web_server/www/README.md: -------------------------------------------------------------------------------- 1 | # ESP32-NAT Router + 2 | 3 | ## What is Jekyll? 4 | 5 | [Jekyll](https://jekyllrb.com/) is a pre-compiler for static content generation that simplifies website maintenance. It uses Ruby to generate static HTML pages, which means that the outputted HTML is the same as if it were written by hand. 6 | 7 | To start using Jekyll, you need to follow these steps: 8 | 9 | - Install Ruby, Gem package manager & Jekyll ( check [here](https://www.ruby-lang.org/ ) ) 10 | - Run `bundle install` in the `/html` folder to install dependencies 11 | 12 | Note : 13 | 14 | Jekyll is a static site generator that uses a specific format to separate the metadata of a web page from its content. At the top of each web file, it's important to include a specific syntax in the form of three hyphens 15 | 16 | ``` 17 | --- 18 | --- 19 | ``` 20 | 21 | This is known as the front matter and it specifies the metadata of the web page, such as the author and the layout styles etc. 22 | 23 | It's important to include these three hyphen lines at the top of each file in order for Jekyll to properly parse the content and avoid errors while running and building the site. Therefore, always make sure to position these lines in the correct place in your web files to ensure a smooth development and build process. 24 | 25 | ## How to run a local server 26 | 27 | Once Jekyll is installed, you can start a local server by following these steps: 28 | 29 | - Run `bundle exec jekyll serve --watch` in the `/html` folder 30 | - Navigate to `http://127.0.0.1:4000` to see the website 31 | - Changes made to files will be reflected in real-time, so you can test and make adjustments to your site's appearance and functionality. (make sure to refresh the page though) 32 | - Alternatively, You can run the server, navigate to the root folder of the project and use `./start_web_server.sh` for Linux terminal or `start_web_server.bat` for Windows. 33 | 34 | ## How to update web-server files? 35 | 36 | ### Build 37 | 38 | - Building is a mandatory step after making changes. you can run the command `JEKYLL_ENV=production bundle exec jekyll build` for Linux Terminal OR 39 | 40 | `set JEKYLL_ENV=production bundle exec jekyll build` for Windows CMD in the `/html` folder to generate and return to production mode minified files. 41 | 42 | - Alternatively, navigate to the root folder of the project and use `./build_web_pages.sh` in Linux terminal or `build_web_pages.bat` in Windows CMD. 43 | 44 | ### Auto Mode (Windows only) 45 | 46 | - Make sure you have Jekyll & Gems installed (see above) 47 | 48 | - Launch `auto_generate.exe` 49 | 50 | - Wait for it to finish 51 | 52 | - That's it **¯\_(ツ)_/¯** 53 | 54 | ### How to Build auto_generate.exe 55 | 56 | The `auto_generate.exe` code is written in JavaScript and converted into an executable file using the `pkg` package for Node.js. 57 | 58 | To build the `auto_generate.exe` file, which automatically generates the website, follow these simple steps: 59 | 1. Install [Node.js](https://nodejs.org) 60 | 2. Open a terminal and run `npm i -g pkg` . 61 | 3. Extract the ZIP file provided in the **additional** folder 62 | 4. Click on `build_auto_generate.bat` or simply run `pkg --compress GZip index.js -o auto_generate.exe` 63 | 5. Wait for some time to download the required files. 64 | 6. That's it! You have successfully built the `auto_generate.exe` file which automatically generates the website. -------------------------------------------------------------------------------- /components/web_server/www/additional/auto_generate.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/additional/auto_generate.zip -------------------------------------------------------------------------------- /components/web_server/www/auto_generate.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/auto_generate.exe -------------------------------------------------------------------------------- /components/web_server/www/html/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: error.title 3 | after-js: fadeIn() 4 | layout: page 5 | --- 6 |
7 |
{% t error.page-header %}
8 |
9 |
10 |
404
11 |
12 |
13 |
14 | 17 | -------------------------------------------------------------------------------- /components/web_server/www/html/APScanResults.json: -------------------------------------------------------------------------------- 1 | { 2 | "aps": [ 3 | { 4 | "c": 10, 5 | "m": "EXAMPLE MAC", 6 | "ss": "Example network 1", 7 | "r": -90, 8 | "e": 0, 9 | "h": 1 10 | }, 11 | { 12 | "c": 9, 13 | "m": "EXAMPLE MAC", 14 | "ss": "Example network 2", 15 | "r": -79, 16 | "e": 2, 17 | "h": 1 18 | }, 19 | { 20 | "c": 10, 21 | "m": "EXAMPLE MAC", 22 | "ss": "Example network 3", 23 | "r": -20, 24 | "e": 3, 25 | "h": 0 26 | }, 27 | { 28 | "c": 10, 29 | "m": "EXAMPLE MAC", 30 | "ss": "Example network 4", 31 | "r": -70, 32 | "e": 4, 33 | "h": 0 34 | }, 35 | { 36 | "c": 10, 37 | "m": "EXAMPLE MAC", 38 | "ss": "Example network 5", 39 | "r": -5, 40 | "e": 1, 41 | "h": 0 42 | }, 43 | { 44 | "c": 10, 45 | "m": "EXAMPLE MAC", 46 | "ss": "Example network 6", 47 | "r": -80, 48 | "e": 0, 49 | "h": 0 50 | }, 51 | { 52 | "c": 10, 53 | "m": "EXAMPLE MAC", 54 | "ss": "Example network 7", 55 | "r": -5, 56 | "e": 1, 57 | "h": 0 58 | }, 59 | { 60 | "c": 6, 61 | "m": "EXAMPLE MAC", 62 | "ss": "....", 63 | "r": -42, 64 | "e": 0, 65 | "h": 0 66 | }, 67 | { 68 | "c": 6, 69 | "m": "EXAMPLE MAC", 70 | "ss": "....", 71 | "r": -42, 72 | "e": 0, 73 | "h": 0 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /components/web_server/www/html/ClientScanResults.json: -------------------------------------------------------------------------------- 1 | { 2 | "clients":[ 3 | { 4 | "i":0, 5 | "p":96, 6 | "m":"11:22:33:44:55:66", 7 | "n":"Name 1", 8 | "l":"0", 9 | "v":"IntelCor", 10 | "s":0, 11 | "a":"EXAMPLE-COMPUTER" 12 | }, 13 | { 14 | "i":1, 15 | "p":12, 16 | "m":"22:33:44:55:66:77", 17 | "n":"", 18 | "l":"-1", 19 | "v":"Samsung", 20 | "s":0, 21 | "a":"EXAMPLE-ANDROID" 22 | }, 23 | { 24 | "i":2, 25 | "p":339, 26 | "m":"33:44:55:66:77:88", 27 | "n":"", 28 | "l":"-1", 29 | "v":"Apple", 30 | "s":0, 31 | "a":"EXAMPLE-PHONE" 32 | }, 33 | { 34 | "i":3, 35 | "p":339, 36 | "m":"00:00:00:00:00:11", 37 | "n":"", 38 | "l":"-1", 39 | "v":"Microsoft", 40 | "s":0, 41 | "a":"EXAMPLE-SAVED" 42 | } 43 | ], 44 | "nameList":[ 45 | { 46 | "n":"SAVED-DEVICE", 47 | "m":"00:00:00:00:00:11" 48 | }, 49 | { 50 | "n":"SAVED-DEVICE2", 51 | "m":"11:00:00:00:00:11" 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /components/web_server/www/html/ClientScanTime.json: -------------------------------------------------------------------------------- 1 | 15 -------------------------------------------------------------------------------- /components/web_server/www/html/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # If you have any plugins, put them here! 4 | group :jekyll_plugins do 5 | gem 'jekyll-tidy' 6 | gem 'jekyll-multiple-languages-plugin' 7 | # gem 'jekyll-gzip' 8 | gem 'jekyll-minifier' 9 | 10 | end 11 | 12 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 13 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 14 | 15 | -------------------------------------------------------------------------------- /components/web_server/www/html/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.1) 5 | public_suffix (>= 2.0.2, < 6.0) 6 | colorator (1.1.0) 7 | concurrent-ruby (1.2.0) 8 | cssminify2 (2.0.1) 9 | em-websocket (0.5.3) 10 | eventmachine (>= 0.12.9) 11 | http_parser.rb (~> 0) 12 | eventmachine (1.2.7) 13 | execjs (2.8.1) 14 | ffi (1.15.5) 15 | forwardable-extended (2.6.0) 16 | google-protobuf (3.24.2-x64-mingw-ucrt) 17 | htmlbeautifier (1.4.2) 18 | htmlcompressor (0.4.0) 19 | http_parser.rb (0.8.0) 20 | i18n (1.12.0) 21 | concurrent-ruby (~> 1.0) 22 | jekyll (4.3.2) 23 | addressable (~> 2.4) 24 | colorator (~> 1.0) 25 | em-websocket (~> 0.5) 26 | i18n (~> 1.0) 27 | jekyll-sass-converter (>= 2.0, < 4.0) 28 | jekyll-watch (~> 2.0) 29 | kramdown (~> 2.3, >= 2.3.1) 30 | kramdown-parser-gfm (~> 1.0) 31 | liquid (~> 4.0) 32 | mercenary (>= 0.3.6, < 0.5) 33 | pathutil (~> 0.9) 34 | rouge (>= 3.0, < 5.0) 35 | safe_yaml (~> 1.0) 36 | terminal-table (>= 1.8, < 4.0) 37 | webrick (~> 1.7) 38 | jekyll-minifier (0.1.10) 39 | cssminify2 (~> 2.0) 40 | htmlcompressor (~> 0.4) 41 | jekyll (>= 3.5) 42 | json-minify (~> 0.0.3) 43 | uglifier (~> 4.1) 44 | jekyll-multiple-languages-plugin (1.8.0) 45 | jekyll (>= 2.0, < 5.0) 46 | jekyll-sass-converter (3.0.0) 47 | sass-embedded (~> 1.54) 48 | jekyll-tidy (0.2.2) 49 | htmlbeautifier 50 | htmlcompressor 51 | jekyll 52 | jekyll-watch (2.2.1) 53 | listen (~> 3.0) 54 | json (2.6.1) 55 | json-minify (0.0.3) 56 | json (> 0) 57 | kramdown (2.4.0) 58 | rexml 59 | kramdown-parser-gfm (1.1.0) 60 | kramdown (~> 2.0) 61 | liquid (4.0.4) 62 | listen (3.8.0) 63 | rb-fsevent (~> 0.10, >= 0.10.3) 64 | rb-inotify (~> 0.9, >= 0.9.10) 65 | mercenary (0.4.0) 66 | pathutil (0.16.2) 67 | forwardable-extended (~> 2.6) 68 | public_suffix (5.0.1) 69 | rb-fsevent (0.11.2) 70 | rb-inotify (0.10.1) 71 | ffi (~> 1.0) 72 | rexml (3.2.5) 73 | rouge (4.0.1) 74 | safe_yaml (1.0.5) 75 | sass-embedded (1.57.1-x64-mingw-ucrt) 76 | google-protobuf (~> 3.21) 77 | terminal-table (3.0.2) 78 | unicode-display_width (>= 1.1.1, < 3) 79 | tzinfo (2.0.5) 80 | concurrent-ruby (~> 1.0) 81 | tzinfo-data (1.2022.7) 82 | tzinfo (>= 1.0.0) 83 | uglifier (4.2.0) 84 | execjs (>= 0.3.0, < 3) 85 | unicode-display_width (2.4.2) 86 | webrick (1.7.0) 87 | 88 | PLATFORMS 89 | x64-mingw-ucrt 90 | 91 | DEPENDENCIES 92 | jekyll-minifier 93 | jekyll-multiple-languages-plugin 94 | jekyll-tidy 95 | tzinfo-data 96 | 97 | BUNDLED WITH 98 | 2.4.4 99 | -------------------------------------------------------------------------------- /components/web_server/www/html/_config.yml: -------------------------------------------------------------------------------- 1 | # Exclude some folders & files 2 | baseurl: "" 3 | exclude: ["_*", "package.json", "node_modules", "Gemfile*"] 4 | destination: ../output/html 5 | 6 | # Language plugin 7 | languages: ["english"] 8 | # "german", "russian", "italian", "dutch", "chinese", "portuguese", "slovak", "polish", "estonian", "hebrew", "czech", "turkish", "indonesia"] 9 | 10 | 11 | # Jekyll tidy HTML plugin 12 | jekyll_tidy: 13 | exclude: ["*.js"] 14 | 15 | 16 | # Gem plugin(s) 17 | plugins: [jekyll-tidy, jekyll-multiple-languages-plugin, jekyll-minifier] 18 | 19 | # Compress outputted sass 20 | sass: 21 | style: compressed 22 | 23 | 24 | jekyll-minifier: 25 | compress_javascript: false 26 | exclude: "*.json" 27 | uglifier_args: 28 | harmony: true 29 | 30 | 31 | # Set port 32 | port: 4000 33 | # Enable access from local network ~ not just localhost 34 | #host: 0.0.0.0 35 | -------------------------------------------------------------------------------- /components/web_server/www/html/_i18n/english.yml: -------------------------------------------------------------------------------- 1 | ################################## 2 | # # 3 | # TRANSLATION FILE # 4 | # # 5 | ################################## 6 | # 7 | # Format: 8 | # What is '$$'? 9 | # '$$' is a variable thats replaced with HTML 10 | # For example: 11 | # `$$click here$$` => `click here` 12 | # `translated by $$` => `translated by sam` 13 | # 14 | # DO NOT REMOVE THE INDENTATION! 15 | # The indentation is critical to YAML, so please don't change it 16 | # 17 | # Try and retain the case, for example: 18 | # If it is capitalized keep it capitalized 19 | # 20 | # 21 | # What encoding should I use? 22 | # UTF-8 ~ jekyll doesn't support any other encodings 23 | # 24 | 25 | 26 | nav: 27 | scan: "Scan" 28 | settings: "Settings" 29 | info: "Info" 30 | 31 | global: 32 | save: "Save" 33 | reset: "Reset" 34 | ssid: "SSID" 35 | password: "Password" 36 | reboot: "Reboot" 37 | guide: "Guide" 38 | footer: "Designed by Jaya Satish - @gjroots" 39 | connect: "Connect" 40 | 41 | scan: 42 | title: "Scan" 43 | selectssid: "Select SSID" 44 | card-1: 45 | header: "Scan for networks ($$)" 46 | table: 47 | signal: "Signal" 48 | security: "Security" 49 | channel: "Ch." # Try and abbreviate, column is small 50 | card-2: 51 | header: "To connect, click on the SSID, If password is less than 8, it is open." 52 | strings: 53 | A: "Open" 54 | B: "INFO: No Wi-Fi network selected!" 55 | messages: 56 | E0: "Failed to parse scan results!" 57 | E1: "ERROR: Failed to scan for networks!" 58 | E2: "Reconnect to Wi-Fi network" 59 | E3: "Failed to save settings!" 60 | E4: "ERROR: Failed to save settings!" 61 | 62 | 63 | settings: 64 | title: "Settings" 65 | card-1: 66 | header: "Customization" 67 | toggle-1: "Enable dark mode" 68 | toggle-2: "Enable LED indicator" 69 | toggle-3: "Enable Web server" 70 | toggle-4: "Enable MAC filter" 71 | card-2: 72 | header: "Admin Settings" 73 | field-1: "Username" 74 | field-2: "Password" 75 | field-3: "Login failed attempts" 76 | field-4: "Lock-out period (minutes)" 77 | card-3: 78 | header: "AP Settings" 79 | card-4: 80 | header: "STA Settings
(WPA2 Enterprise: Leave it blank for regular)" 81 | field-1: "Enterprise Username" 82 | field-2: "Enterprise Identity" 83 | card-5: 84 | header: "Static Settings" 85 | field-1: "Static IP" 86 | field-2: "Subnet Mask" 87 | field-3: "Gateway" 88 | card-6: 89 | header: "Advance Settings" 90 | field-1: "AP IP" 91 | field-2: "DNS IP" 92 | field-3: "MAC address" 93 | toggle-1: "Enable custom DNS" 94 | toggle-2: "Random MAC address" 95 | 96 | 97 | strings: 98 | A: "Are you sure reset to default settings?" 99 | B: "All changes will be lost!" 100 | messages: 101 | E0: "Failed to save settings!" 102 | E1: "Failed to reset settings!" 103 | E2: "ERROR: Failed to load settings.json" 104 | E3: "ERROR: Failed to save settings!" 105 | E4: "ERROR: Reset the settings" 106 | info: 107 | title: "Info" 108 | card-1: 109 | header: "ESP32-NAT Router +" 110 | table: 111 | A: "version" 112 | B: "Update" 113 | C: "IP / DNS" 114 | card-2: 115 | header: "Connected Users ($$)" 116 | table: 117 | A: "IP Address" 118 | B: "MAC Address" 119 | C: "Add" 120 | card-3: 121 | header: "Acknowledgements" 122 | card-4: 123 | header: "MAC Filter($$)" 124 | table: 125 | A: "ID" 126 | B: "MAC Address" 127 | C: "Remove" 128 | strings: 129 | B: "OTA update" 130 | C: "upload" 131 | D: "Other projects" 132 | E: "Report bug" 133 | F: "Other projects" 134 | messages: 135 | message-1: "file uploading Status: $$" 136 | message-2: "OTA Update Success - Restarting" 137 | message-3: "Settings have been saved." 138 | E0: "ERROR: Failed to load sysinfo.json" 139 | E1: "ERROR: OTA update ERROR" 140 | E2: "Failed to save settings!" 141 | E3: "ERROR: Failed to save settings!" 142 | 143 | error: 144 | title: "404" 145 | page-header: "Page not found" 146 | 147 | errors: 148 | bad-response: "ERROR: Bad response" 149 | E11: "Reconnect and reload this page" 150 | E12: "Reconnect to Wi-Fi network" 151 | E13: "Failed to restart Router!" 152 | E14: "Didn't receive a response!" 153 | E15: "Reconnect and reload this page" 154 | S1: "Are you sure you want to reboot?" 155 | S2: "Wi-Fi network Connected" 156 | S3: "Wi-Fi network Connected Reloading...." 157 | -------------------------------------------------------------------------------- /components/web_server/www/html/_includes/footer.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | {% if page.js %}{% endif %} 5 | {% if page.after-js %}{% endif %} -------------------------------------------------------------------------------- /components/web_server/www/html/_includes/inline.js: -------------------------------------------------------------------------------- 1 | {% if page.inline-js %}{{ page.inline-js }}{% endif %} 2 | /(^|;)\s*minimal=/.test(document.cookie) && (document.body.id = "minimal"); -------------------------------------------------------------------------------- /components/web_server/www/html/_includes/nav.html: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /components/web_server/www/html/_layouts/html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% t page.title %} - ESP32-NAT Router + 4 | 5 | 6 | {% if page.url != "/setup.html" %}{% endif %} 7 | 8 | 9 | 10 | 11 | 12 | {{ content }} 13 | 14 | 15 | -------------------------------------------------------------------------------- /components/web_server/www/html/_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: html 3 | --- 4 | {% include nav.html %} 5 |
6 | {% if page.no-container == null %}
{% endif %} 7 | {{ content }} 8 | {% if page.no-container == null %}
{% endif %} 9 | {% include footer.html %} 10 |
-------------------------------------------------------------------------------- /components/web_server/www/html/dark.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | #logo-img { 4 | background: none 5 | } 6 | #nav { 7 | background: #1b1b21 8 | } 9 | body { 10 | color: #fff; 11 | background: #28282e !important 12 | } 13 | nav .main-header { 14 | background: #1b1b21 !important 15 | } 16 | .button, button, input[type='submit'], .reboot { 17 | background: #61616f 18 | } 19 | .button:hover, button:hover, input[type='submit']:hover, .reboot:hover { 20 | background: #6c6c79 21 | } 22 | .secondary, .card-action a { 23 | color: #8aa0d4 !important; 24 | background: transparent; 25 | } 26 | .card-action a:hover, .secondary:hover { 27 | background: rgba(255, 255, 255, .03); 28 | } 29 | *, .input-container label { 30 | color: #dadada 31 | } 32 | .settingsHeader, .settingsHeader *, .main-wrap, .WiFi span, td, th, .input-container input:valid + label, .input-container input:invalid:not(:placeholder-shown) + label, .input-container input:focus + label { 33 | color: #dadada !important 34 | } 35 | #apMAC { 36 | background: rgba(255, 255, 255, .1) 37 | } 38 | .card, .card-small { 39 | background: #37373d 40 | } 41 | .WiFi svg { 42 | fill: #fff 43 | } 44 | .WiFi span:nth-child(1) { 45 | box-shadow: inset 0 0 15px rgba(0, 0, 0, .15), 0 0 4px rgba(0, 0, 0, .15); 46 | -webkit-filter: initial; 47 | filter: initial; 48 | } 49 | .WiFi span:nth-child(2) { 50 | -webkit-filter: none; 51 | filter: none; 52 | opacity: 1; 53 | text-shadow: 0px 0px 7px rgba(0, 0, 0, .5); 54 | font-weight: 400 55 | } 56 | label.checkbox { 57 | border-color: #e1e1e1 58 | } 59 | input[type='checkbox']:checked + label { 60 | border-color: #8b8b92; 61 | background: #8b8b92 62 | } 63 | #spinner-container { 64 | background: #3e3e3e 65 | } 66 | .edit { 67 | color: #dadada 68 | } 69 | .edit svg { 70 | fill: #dadada 71 | } 72 | .input-container input:placeholder-shown { 73 | border-color: #dadada 74 | } 75 | .input-container input:focus { 76 | border-color: #fff !important 77 | } 78 | footer { 79 | background: #2a2a31 80 | } 81 | h1 { 82 | color: rgba(255, 255, 255, .66) 83 | } 84 | a, .a { 85 | color: #8aa0d4 86 | } 87 | .light-6 { 88 | opacity: .4; 89 | } 90 | :root { 91 | --theme-color: #1b1b21 92 | } 93 | @media (min-width:520px) { 94 | ::-webkit-scrollbar { 95 | width: 14px; 96 | background-color: #28282E 97 | } 98 | ::-webkit-scrollbar-thumb { 99 | background-color: #4b4b54 100 | } 101 | ::-webkit-scrollbar-thumb:hover { 102 | background-color: #51515a 103 | } 104 | ::-webkit-scrollbar-thumb:active, ::-webkit-scrollbar-thumb:disabled { 105 | background-color: #393942 106 | } 107 | ::-webkit-scrollbar-button { 108 | display: none; 109 | width: 0; 110 | height: 0 111 | } 112 | ::-webkit-scrollbar-corner { 113 | background-color: transparent 114 | } 115 | } 116 | .input-container input:placeholder-shown { 117 | border-bottom: 2px solid #47474e !important; 118 | } 119 | .input-container input:valid { 120 | border-bottom: 2px solid #47474e !important; 121 | } 122 | .input-container input:invalid { 123 | border-bottom: 2px solid #47474e !important; 124 | } 125 | .input-container input:focus { 126 | border-bottom: 2px solid #606065 !important; 127 | } 128 | nav #links a.active::before { 129 | background: #dadada 130 | } 131 | .selected { 132 | background: rgba(255, 255, 255, 0.044) !important; 133 | } 134 | .green { 135 | color: #4caf50 !important 136 | } 137 | .red { 138 | color: #f5716e !important 139 | } 140 | .s-s { 141 | background: #709bbd 142 | } 143 | .s-s .row * { 144 | color: #d5edff 145 | } 146 | hr { 147 | border: 1px solid rgba(0, 0, 0, .1) 148 | } 149 | #notification { 150 | background: #61616f 151 | } 152 | .glitch { 153 | color: red !important 154 | } 155 | .glitch:after { 156 | color: red !important; 157 | background: #37373D !important; 158 | text-shadow: -4px 0 green !important 159 | } 160 | .glitch:before { 161 | color: red !important; 162 | background: #37373D !important; 163 | text-shadow: 4px 0 blue !important 164 | } 165 | .samddAttrib span { 166 | color: #ffdccc !important 167 | } 168 | .samddAttrib span a { 169 | font-weight: bold; 170 | color: #ffbea1 !important 171 | } 172 | .waves-effect.waves-light-color .waves-ripple, .waves-effect.waves-color .waves-ripple { 173 | background: rgba(255, 255, 255, 0.15) 174 | } 175 | .card-header { 176 | svg { 177 | fill: rgba(255, 255, 255, 0.6); 178 | &:hover { 179 | background: rgba(255, 255, 255, 0.05); 180 | } 181 | } 182 | } 183 | .horizontal #logo-img { 184 | background: rgba(0, 0, 0, 0.06); 185 | svg { 186 | fill: rgba(255, 255, 255, 0.62) 187 | } 188 | } -------------------------------------------------------------------------------- /components/web_server/www/html/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: scan.title 3 | inline-js: var u = window.location.href; - 1 != u.indexOf("?minimal=true") && (document.body.id = "minimal", document.cookie = "minimal=true"); 4 | js: scan 5 | layout: page 6 | --- 7 |
8 |
11 | {{site.translations[site.lang].scan.card-1.header | replace_first: '$$', 12 | ''}} 13 |
14 | 15 |
16 |
17 |
18 |
{% t scan.card-2.header %}
19 |
20 | 21 | 22 | 23 | 24 | 29 | 34 | 35 | 36 |
25 |
26 | 27 |
28 |
30 | 33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /components/web_server/www/html/info.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: info.title 3 | js: info 4 | layout: page 5 | --- 6 | 7 |
8 |
{% t info.card-1.header %}
9 |
10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 |
{% t info.card-1.table.A %} 15 | 16 | 17 |
22 | 27 |
28 |
29 | 30 |
31 |
{{site.translations[site.lang].info.card-2.header | replace_first: '$$', 32 | ''}}
33 |
34 |
35 |
36 |
37 |
38 |
{{site.translations[site.lang].info.card-4.header | replace_first: '$$', 39 | ''}}
40 |
41 |
42 |
43 |
44 |
45 |
{% t info.card-3.header %}
46 |
47 |
48 | @martin-ger(martin-ger)
49 | @samdenty (Sam Denty)
50 | @kevinwolfe(Kevin Wolfe)
51 | @nopnop2002
52 | @dchristl
53 | 54 | 55 |
56 | 57 |

58 | This software is based upon the work of martin-ger(ESP32 NAT Router). UI is based on the work of Sam Denty(Wi-PWN).
59 | 60 |

61 |
62 |
-------------------------------------------------------------------------------- /components/web_server/www/html/js/info.js: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | var versionCell = getE("version"), 4 | ipGateway = getE("ipGatewayAddress"), 5 | ssid, 6 | ipAddress, 7 | dns, 8 | connection = getE("connection"), 9 | connectedUserCount = getE('clientsFound'), 10 | connectedUsers = getE('users'), 11 | filterList = getE("filterList"), 12 | filterListCount = getE("filterListCount"), 13 | ap_rss = 0, 14 | wifiAuthFail, 15 | tableHeaderHTML, 16 | otaProgress = false, 17 | filterListType, 18 | clients = [], 19 | filterListData = []; 20 | 21 | function getData() { 22 | if (otaProgress) return; 23 | getResponse('{% if jekyll.environment == "development" %}sysinfo.json{% else %}data/sysinfo.json{% endif %}', 24 | function (responseText) { 25 | notify(); 26 | try { 27 | var res = JSON.parse(responseText); 28 | log("RESPONSE ~ ", res, true) 29 | } catch (err) { 30 | log("INVALID ~ ", responseText, false) 31 | console.error(err) 32 | return 33 | } 34 | // console.log(res.clients.length) 35 | ssid = res.ssid; 36 | ipAddress = res.ipAddress; 37 | dns = res.dns; 38 | ap_rss = res.rss; 39 | wifiAuthFail = res.wifiAuthFail; 40 | clients = res.clients; 41 | versionCell.innerHTML = version; 42 | filterListData = res.filterList; 43 | filterListType = res.filterListType; 44 | fadeIn(); 45 | users(); 46 | ap_connection(); 47 | filter_list(); 48 | 49 | }, function () { 50 | notify('{% t info.messages.E0 %} (E0)'); 51 | fadeIn(); 52 | }); 53 | } 54 | 55 | function bytesToSize(bytes) { 56 | var sizes = [' bytes', 'KB', 'MB', 'GB', 'TB']; 57 | if (bytes == 0) return '0 Byte'; 58 | var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); 59 | return Math.round(bytes / Math.pow(1024, i), 2) + '' + sizes[i]; 60 | }; 61 | 62 | getData(); 63 | infoInterval = setInterval(getData, 3000); 64 | 65 | 66 | 67 | function upload_file() { 68 | var data = getE("file_select").files[0]; 69 | 70 | request = new XMLHttpRequest(); 71 | 72 | request.open("POST", "/ota", true); 73 | 74 | request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 75 | 76 | request.upload.addEventListener("progress", function (event) { 77 | otaProgress = true; 78 | 79 | if (event.lengthComputable) { 80 | var progress = Math.floor((event.loaded / event.total) * 100); 81 | notify("{{site.translations[site.lang].info.messages.message-1 | replace_first: '$$', '" + progress + " %'}}"); 82 | showLoading(); 83 | } 84 | 85 | }); 86 | 87 | request.onreadystatechange = function () { 88 | 89 | if (request.readyState === XMLHttpRequest.DONE) { 90 | 91 | var status = request.status; 92 | 93 | if (status >= 200 && status < 400) { 94 | 95 | notify("{% t info.messages.message-2 %}", 2000); 96 | fadeIn(); 97 | indicate(true); 98 | otaProgress = false; 99 | autoReload(); 100 | 101 | } else { 102 | 103 | notify("{% t info.messages.E1 %} (E1)", 2000) 104 | fadeIn(); 105 | indicate(false); 106 | data = ""; 107 | otaProgress = false; 108 | autoReload(); 109 | 110 | } 111 | 112 | } 113 | 114 | }; 115 | 116 | request.send(data); 117 | return false; 118 | } 119 | 120 | function getColor(value, lighten) { 121 | var lightness = 50; 122 | var saturation = 75; 123 | if (lighten == true) lightness = 90 124 | if (value > 120) value = 100 125 | if (value > 90) saturation = 60; 126 | value = 100 - value; 127 | var hue = ((1 - (value / 87)) * 100).toString(10); 128 | return ["hsl(", hue, "," + saturation + "%," + lightness + "%)"].join(""); 129 | } 130 | 131 | function ap_connection() { 132 | var state = (ap_rss != 0); 133 | var svgImg = state ? soC : soD; 134 | var signalPercent = ap_rss; 135 | if (ap_rss != 0) { 136 | signalPercent = Math.round((1 - ((ap_rss + 30) / -70)) * 100); 137 | if (signalPercent > 100) signalPercent = 100; 138 | } 139 | 140 | var tr = 'Wifi status'; 141 | tr += '
' + svgImg + '
' + signalPercent + '
'; 142 | tr += '' + (state ? ssid : (wifiAuthFail ? "wifi Auth Fail" : 'Disconnected')) + ''; 143 | connection.innerHTML = tr; 144 | 145 | tr = '{% t info.card-1.table.C %}' + ipAddress + '' + dns + ''; 146 | ap_rss != 0 ? ipGateway.innerHTML = tr : ""; 147 | } 148 | function users() { 149 | console.log("clients: ", clients.length) 150 | connectedUserCount.innerHTML = clients.length; 151 | var tr = ''; 152 | if (clients.length > 0) { 153 | tableHeaderHTML = '{% t info.card-2.table.A %}{% t info.card-2.table.B %}{% t info.card-2.table.C %}'; 154 | tr += tableHeaderHTML; 155 | 156 | for (var i = 0; i < clients.length; i++) { 157 | var id = i + 1; 158 | tr += '' + clients[i].ipAddress + ' ' + clients[i].macAddress + ''; 159 | connectedUsers.innerHTML = tr; 160 | } 161 | } 162 | } 163 | 164 | 165 | function filter_list() { 166 | console.log("clients: ", filterListData.length) 167 | filterListCount.innerHTML = filterListData.length; 168 | var tr = ''; 169 | tableHeaderHTML = '{% t info.card-4.table.A %}{% t info.card-4.table.B %}{% t info.card-4.table.C %}'; 170 | tr += tableHeaderHTML; 171 | filterList.innerHTML = tr; 172 | if (filterListData.length > 0) { 173 | 174 | for (var i = 0; i < filterListData.length; i++) { 175 | var id = i + 1; 176 | tr += '' + id + '' + filterListData[i] + ' '; 177 | filterList.innerHTML = tr; 178 | } 179 | } 180 | var IsAllowList = (filterListType === "Allow"); 181 | tr += '' 182 | tr += ''; 183 | tr += ''; 184 | tr += '' 185 | filterList.innerHTML = tr; 186 | } 187 | 188 | 189 | function saveSettings(data) { 190 | var url = '{% if jekyll.environment == "development" %}settingsSave.json{% else %}data/settingsSave.json{% endif %}'; 191 | url += data; 192 | showLoading(); 193 | getResponse(url, function (responseText) { 194 | if (responseText == "true") { 195 | indicate(true); 196 | notify("{% t info.messages.message-3 %} (M3)", 2000); 197 | } else { 198 | indicate(false); 199 | notify("{% t info.messages.E2 %} (E2)"); 200 | } 201 | }, function () { 202 | indicate(false); 203 | notify("{% t info.messages.E3 %} (E3)"); 204 | }, null, "POST"); 205 | } 206 | 207 | 208 | function add_mac_to_filter_list(num){ 209 | var data = "?add_mac_address=" + encodeURI(clients[num].macAddress); 210 | saveSettings(data); 211 | } 212 | 213 | function remove_mac_to_filter_list(num){ 214 | var data = "?remove_mac_address=" + encodeURI(filterListData[num]); 215 | saveSettings(data); 216 | } 217 | 218 | function filter_list_type(){ 219 | var data = "?filter_list_type="+ encodeURI(getE("Allow").checked ? "Allow":"Deny") 220 | saveSettings(data); 221 | } 222 | -------------------------------------------------------------------------------- /components/web_server/www/html/js/scan.js: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | var table = document.getElementsByTagName('table')[0], 4 | networkInfo = getE('networksFound'), 5 | scanInfo = getE('spinner-container'), 6 | apMAC = getE('apMAC'), 7 | selectAll = getE('selectAll'), 8 | ssid = getE("ssid"), 9 | password = getE("password"), 10 | // autoScan = true, 11 | tableHeaderHTML = '{% t scan.card-1.table.signal %}{% t global.ssid %}{% t scan.card-1.table.security %}{% t scan.card-1.table.channel %}', 12 | selectedApElement = -1, 13 | previousCall = new Date().getTime(), 14 | url = window.location.href, 15 | wifiIndicator, securityState, 16 | scanning = false, res; 17 | 18 | function toggleScan(onoff) { 19 | if (onoff) { 20 | showLoading("hide"); 21 | scanning = false; 22 | } else { 23 | showLoading(); 24 | scanning = true; 25 | } 26 | } 27 | 28 | function compare(a, b) { 29 | if (a.r > b.r) return -1; 30 | if (a.r < b.r) return 1; 31 | return 0; 32 | } 33 | 34 | 35 | // WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */ 36 | // WIFI_AUTH_WEP = 1, /**< authenticate mode : WEP */ 37 | // WIFI_AUTH_WPA_PSK = 2, /**< authenticate mode : WPA_PSK */ 38 | // WIFI_AUTH_WPA2_PSK = 3, /**< authenticate mode : WPA2_PSK */ 39 | // WIFI_AUTH_WPA_WPA2_PSK = 4, /**< authenticate mode : WPA_WPA2_PSK */ 40 | // WIFI_AUTH_WPA2_ENTERPRISE = 5, /**< authenticate mode : WPA2_ENTERPRISE */ 41 | // WIFI_AUTH_WPA3_PSK = 6, /**< authenticate mode : WPA3_PSK */ 42 | // WIFI_AUTH_WPA2_WPA3_PSK = 7, /**< authenticate mode : WPA2_WPA3_PSK */ 43 | // WIFI_AUTH_WAPI_PSK, 44 | 45 | function getStatus(enc, hid) { 46 | var buff = ""; 47 | if (enc == 0) buff = "Open"; 48 | else if (enc == 1) buff = "Wep 🔒"; 49 | else if (enc == 2) buff = "Wpa 🔒"; 50 | else if (enc == 3) buff = "Wpa2  🔒"; 51 | else if (enc == 4) buff = "Wpa / Wpa2 🔒"; 52 | else if (enc == 5) buff = "WPA2 EP;🔒"; 53 | else if (enc == 6) buff = "Wpa3_PSK 🔒"; 54 | else if (enc == 7) buff = "Wpa2 / Wpa3 🔒"; 55 | if (hid == 1) buff += "👻"; 56 | return buff 57 | } 58 | function scan() { 59 | if (scanning) return; 60 | toggleScan(false); 61 | getResponse('{% if jekyll.environment == "development" %}APScanResults.json{% else %}data/APScanResults.json{% endif %}', function (responseText, status) { 62 | try { 63 | if (responseText == "false") return setTimeout(function () { scanning = false;scan(); }, 1000); 64 | res = JSON.parse(responseText); 65 | log("RESPONSE ~ ", res, true) 66 | notify(); 67 | toggleScan(true); 68 | } catch (err) { 69 | log("INVALID ~ ", responseText, false) 70 | console.error(err) 71 | notify('{% t scan.messages.E0 %} (E0)'); 72 | scanning = false; 73 | return 74 | } 75 | res.aps = res.aps.sort(compare); 76 | networkInfo.innerHTML = res.aps.length; 77 | // if (res.aps.length == 0) scan() 78 | apMAC.innerHTML = ""; 79 | if (res.multiAPs == 1) tableHeaderHTML = '{% t scan.card-1.table.signal %}{% t global.ssid %}{% t scan.card-1.table.security %}{% t scan.card-1.table.channel %}'; 80 | var tr = ''; 81 | if (res.aps.length > 0) tr += tableHeaderHTML; 82 | 83 | for (var i = 0; i < res.aps.length; i++) { 84 | 85 | // if (res.aps[i].se == 1) tr += ''; 86 | tr += ''; 87 | 88 | if (getStatus(res.aps[i].e) === "Open") { securityState = '' } else { securityState = 'L' } 89 | if (-89 > res.aps[i].r) { 90 | wifiIndicator = 's0' + securityState 91 | } else if (-88 > res.aps[i].r) { 92 | wifiIndicator = 's1' + securityState 93 | } else if (-77 > res.aps[i].r) { 94 | wifiIndicator = 's2' + securityState 95 | } else if (-66 > res.aps[i].r) { 96 | wifiIndicator = 's3' + securityState 97 | } else { 98 | wifiIndicator = 's4' + securityState 99 | } 100 | 101 | var signalPercent = Math.round((1 - ((res.aps[i].r + 30) / -70)) * 100); 102 | if (signalPercent > 100) signalPercent = 100; 103 | if (i == 0) { var tdID = ' id="resizeEventTD"' } else { var tdID = '' } 104 | tr += '
' + eval(wifiIndicator) + '
' + signalPercent + '
'; 105 | tr += '' + escapeHTML(res.aps[i].ss) + ''; 106 | tr += '' + getStatus(res.aps[i].e, res.aps[i].h) + ''; 107 | tr += '' + res.aps[i].c + ''; 108 | tr += ''; 109 | } 110 | table.innerHTML = tr; 111 | checkSize() 112 | fadeIn(); 113 | }, function () { 114 | toggleScan(true); 115 | fadeIn(); 116 | notify("{% t scan.messages.E1 %} (E1)") 117 | }); 118 | } 119 | 120 | function getColor(value, lighten) { 121 | var lightness = 50; 122 | var saturation = 75; 123 | if (lighten == true) lightness = 90 124 | if (value > 120) value = 100 125 | if (value > 90) saturation = 60; 126 | value = 100 - value; 127 | var hue = ((1 - (value / 87)) * 100).toString(10); 128 | return ["hsl(", hue, "," + saturation + "%," + lightness + "%)"].join(""); 129 | } 130 | 131 | 132 | function select(num) { 133 | el = getE("tr" + num); 134 | getE("ssid").innerText = res.aps[num].ss; 135 | if (selectedApElement != -1) { 136 | selectedApElement.classList.remove("selected") 137 | } 138 | selectedApElement = el; 139 | el.classList.add("selected"); 140 | getE("password").focus(); 141 | 142 | } 143 | 144 | 145 | function connect() { 146 | if (selectedApElement == -1) return alert("{% t scan.strings.B %}"); 147 | showLoading(); 148 | var url = '{% if jekyll.environment == "development" %}settingsSave.json{% else %}data/settingsSave.json{% endif %}'; 149 | url += "?ssid=" + encodeURI(ssid.innerText); 150 | url += "&password=" + encodeURI(password.value); 151 | getResponse(url, function (responseText) { 152 | if (responseText == "true") { 153 | indicate(true); 154 | restart(true); 155 | notify("{% t scan.messages.E2 %} (E2)", 2000); 156 | } else { 157 | indicate(false); 158 | notify("{% t scan.messages.E3 %} (E3)"); 159 | } 160 | }, function () { 161 | ndicate(false); 162 | notify("{% t scan.messages.E4 %} (E4)"); 163 | }, null, "POST"); 164 | } 165 | 166 | 167 | /* Add event listener for WiFi signal icons */ 168 | window.onresize = function (event) { checkSize() } 169 | function checkSize() { 170 | try { 171 | var w = document.getElementById('resizeEventTD'); 172 | if (w.clientWidth <= 99) { 173 | document.getElementById('apscan').className = 'pointUp' 174 | } else { 175 | document.getElementById('apscan').className = '' 176 | } 177 | } catch (e) { } 178 | } 179 | scan(); -------------------------------------------------------------------------------- /components/web_server/www/html/js/settings.js: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | var ssid = getE('ssid'), 4 | password = getE('password'), 5 | ent_username = getE('enterprise_username'), 6 | ent_identity = getE('enterprise_identity'), 7 | apSsid = getE('apSsid'), 8 | apPassword = getE('apPassword'), 9 | macAp = getE('macAp'), 10 | randMacAp = getE('randMacAp'), 11 | staticIP = getE('staticIP'), 12 | subnetMask = getE('subnetMask'), 13 | gateway = getE('gateway'), 14 | dnsIP = getE('dnsIp'), 15 | CustomDns = getE('CustomDns'), 16 | darkMode = getE('darkMode'), 17 | ledEnable = getE('useLed'), 18 | webServer = getE('webServer'), 19 | macFilter = getE('macFilter'), 20 | apIP = getE('apIP'), 21 | macContainer = getE('macContainer'), 22 | dnsIpContainer = getE('dnsIpContainer'), 23 | nextDnsUrlContainer = getE('nextDnsUrlContainer'), 24 | adminUsername = getE('adminUsername'), 25 | adminPassword = getE('adminPassword'), 26 | maxLoginAttempts = getE('maxLoginAttempts'), 27 | blockingTimeMin = getE('blockingTimeMin'), 28 | res = '', 29 | checkboxChanges, 30 | inputChanges; 31 | 32 | /* Add listeners to checkboxes */ 33 | 34 | randMacAp.addEventListener("change", switchMAC, false); 35 | CustomDns.addEventListener("change", dns_Ip, false); 36 | 37 | function getData() { 38 | getResponse('{% if jekyll.environment == "development" %}settings.json{% else %}data/settings.json{% endif %}', function (responseText) { 39 | // try { 40 | // res = JSON.parse(responseText); 41 | // } catch (e) { 42 | // fadeIn(); 43 | // notify("{% t settings.messages.E2 %} (E2)"); 44 | // return; 45 | // } 46 | try { 47 | res = JSON.parse(responseText); 48 | log("RESPONSE ~ ", res, true) 49 | } catch (err) { 50 | log("INVALID ~ ", responseText, false) 51 | console.error(err) 52 | fadeIn(); 53 | notify("{% t settings.messages.E2 %} (E2)"); 54 | return 55 | } 56 | ssid.value = res.ssid; 57 | password.value = res.password; 58 | ent_username.value = res.entUsername; 59 | ent_identity.value = res.entIdentity; 60 | apSsid.value = res.apSsid; 61 | apPassword.value = res.apPassword; 62 | staticIP.value = res.staticIP; 63 | subnetMask.value = res.subnetMask; 64 | gateway.value = res.gateWay; 65 | macAp.value = res.macAp; 66 | apIP.value = res.apIP; 67 | dnsIP.value = res.dnsIP; 68 | randMacAp.checked = res.randMacAp; 69 | CustomDns.checked = res.CustomDns; 70 | ledEnable.checked = res.ledEnable; 71 | darkMode.checked = res.darkMode; 72 | webServer.checked = res.webServer; 73 | macFilter.checked = res.macFilterEnabled; 74 | adminUsername.value = res.authUsername; 75 | adminPassword.value = res.authPassword 76 | maxLoginAttempts.value = res.maxLoginAttempts 77 | blockingTimeMin.value = res.blockingTimeMin 78 | switchMAC(); 79 | dns_Ip(); 80 | // nextDNS(); 81 | fadeIn(); 82 | }, function () { 83 | notify("{% t settings.messages.E4 %} (E4)"); 84 | fadeIn(); 85 | }); 86 | } 87 | 88 | function saveSettings() { 89 | showLoading(); 90 | var url = '{% if jekyll.environment == "development" %}settingsSave.json{% else %}data/settingsSave.json{% endif %}'; 91 | url += "?ssid=" + encodeURI(ssid.value); 92 | url += "&password=" + encodeURI(password.value); 93 | url += "&ent_username=" + encodeURI(ent_username.value); 94 | url += "&ent_password=" + encodeURI(ent_identity.value); 95 | url += "&apSsid=" + encodeURI(apSsid.value); 96 | url += "&apPassword=" + encodeURI(apPassword.value); 97 | url += "&staticIP=" + staticIP.value; 98 | url += "&subnetMask=" + subnetMask.value; 99 | url += "&gateway=" + gateway.value; 100 | url += "&apIP=" + apIP.value; 101 | url += "&macAp=" + macAp.value; 102 | url += "&dnsIP=" + dnsIp.value; 103 | url += "&adminUsername=" + encodeURI(adminUsername.value); 104 | url += "&adminPassword=" + encodeURI(adminPassword.value); 105 | url += "&maxLoginAttempts=" + maxLoginAttempts.value; 106 | url += "&blockingTimeMin=" + blockingTimeMin.value; 107 | url += "&ledEnable=" + ledEnable.checked; 108 | url += "&randMacAp=" + randMacAp.checked; 109 | url += "&CustomDns=" + CustomDns.checked; 110 | url += "&darkMode=" + darkMode.checked; 111 | url += "&webServer=" + webServer.checked; 112 | url += "&macFilterEnable=" + macFilter.checked; 113 | 114 | 115 | getResponse(url, function (responseText) { 116 | if (responseText == "true") { 117 | getData(); 118 | indicate(true); 119 | var uniqueKey = new Date(); 120 | document.getElementById('darkStyle').setAttribute('href', 'dark.css?' + uniqueKey.getTime()); 121 | defaultMetaColor(); 122 | inputChanges = false; 123 | checkboxChanges = false; 124 | } else { 125 | indicate(false); 126 | notify("{% t settings.messages.E0 %} (E0)", 2000); 127 | } 128 | }, function () { 129 | getResponse('{% if jekyll.environment == "development" %}settings.json{% else %}data/settings.json{% endif %}', function (responseText) { 130 | getData(); 131 | indicate(true); 132 | var uniqueKey = new Date(); 133 | document.getElementById('darkStyle').setAttribute('href', 'dark.css?' + uniqueKey.getTime()); 134 | defaultMetaColor(); 135 | inputChanges = false; 136 | checkboxChanges = false; 137 | }, function () { 138 | indicate(false); 139 | notify("{% t settings.messages.E3 %} (E3)"); 140 | }); 141 | },null,"POST"); 142 | } 143 | 144 | function resetSettings() { 145 | if (confirm("{% t settings.strings.A %}") == true) { 146 | showLoading(); 147 | getResponse('{% if jekyll.environment == "development" %}settingsReset.json{% else %}data/settingsReset.json{% endif %}', function (responseText) { 148 | if (responseText == "true") { 149 | getData(); 150 | indicate(true); 151 | restart(true); 152 | } else { 153 | notify("{% t settings.messages.E1 %} (E1)", 2500); 154 | indicate(false); 155 | } 156 | }, function () { 157 | notify("{% t settings.messages.E4 %} (E4)"); 158 | indicate(false); 159 | }); 160 | } 161 | inputChanges = false; 162 | checkboxChanges = false; 163 | } 164 | 165 | 166 | function switchMAC() { 167 | if (randMacAp.checked) 168 | macContainer.classList.add("disabled"); 169 | else 170 | macContainer.classList.remove("disabled"); 171 | } 172 | function dns_Ip() { 173 | if (CustomDns.checked) { 174 | dnsIpContainer.classList.remove("disabled"); 175 | } else { 176 | dnsIpContainer.classList.add("disabled"); 177 | } 178 | } 179 | 180 | getData(); 181 | 182 | /* Detect form changes and display popup if not saved */ 183 | var form = document.getElementById("settings"); 184 | form.addEventListener("input", function () { 185 | inputChanges = true; 186 | }); 187 | form.addEventListener("change", function () { 188 | checkboxChanges = true; 189 | }, false); 190 | 191 | window.addEventListener("beforeunload", function (e) { 192 | if (inputChanges || checkboxChanges) { 193 | var confirmationMessage = '{% t settings.strings.B %}'; 194 | (e || window.event).returnValue = confirmationMessage; 195 | return confirmationMessage; 196 | } 197 | }); 198 | -------------------------------------------------------------------------------- /components/web_server/www/html/reloadSSID.json: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /components/web_server/www/html/restart.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /components/web_server/www/html/settings.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: settings.title 3 | js: settings 4 | no-container: true 5 | layout: page 6 | --- 7 | 8 |
9 |
10 |
{% t settings.card-1.header %}
11 |
12 |
13 |
14 | 19 |
20 |
21 | 25 |
26 |
27 | 31 |
32 |
33 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
{% t settings.card-2.header %}
44 |
45 |
46 |
47 |
48 | 49 | 50 |
51 |
52 |
53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 | 61 | 62 |
63 |
64 |
65 |
66 | 67 | 68 |
69 |
70 |
71 |
72 |
73 | 74 |
75 |
{% t settings.card-3.header %}
76 |
77 |
78 |
79 |
80 | 81 | 82 |
83 |
84 |
85 |
86 | 87 | 88 |
89 |
90 |
91 |
92 |
93 | 94 |
95 |
{% t settings.card-4.header %}
96 |
97 |
98 |
99 |
100 | 101 | 102 |
103 |
104 |
105 |
106 | 107 | 108 |
109 |
110 |
111 |
112 | 113 | 114 |
115 |
116 |
117 |
118 | 119 | 120 |
121 |
122 |
123 |
124 |
125 | 126 | 127 |
128 |
{% t settings.card-5.header %}
129 |
130 |
131 |
132 |
133 | 134 | 135 |
136 |
137 |
138 |
139 | 140 | 141 |
142 |
143 |
144 |
145 | 146 | 147 |
148 |
149 |
150 |
151 |
152 | 153 |
154 |
{% t settings.card-6.header %}
155 |
156 |
157 |
158 |
159 | 160 | 161 |
162 |
163 |
164 |
165 | 166 | 167 |
168 |
169 |
170 | 174 |
175 |
176 |
177 | 178 | 179 |
180 |
181 |
182 | 186 |
187 |
188 |
189 |
190 | 191 |
192 | 193 | 194 |
195 |
196 | -------------------------------------------------------------------------------- /components/web_server/www/html/settings.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "apSsid": "ESP32_NAT_Router +", 4 | "apPassword": "temp", 5 | "epUsername": "121212", 6 | "epIdentity": "121212", 7 | "ssid": "Stass", 8 | "password": "stapass", 9 | "apIP": "192.168.4.1", 10 | "staticIP": "", 11 | "subnetMask": "", 12 | "gateWay": "", 13 | "macAp": "ca:2c:mc:cn:b2", 14 | "dnsIP": "1.0.0.1", 15 | "nextDnsUrl": "https://linkip.nextdns.com/XXXXXXXX", 16 | "randMacAp": true, 17 | "CustomDns": false, 18 | "nextDns": false, 19 | "ledEnable": true, 20 | "darkMode": true, 21 | "webServer": true, 22 | "authUsername": "admin", 23 | "authPassword": "123456789", 24 | "maxLoginAttempts": 5, 25 | "blockingTimeMin": 1 26 | } 27 | -------------------------------------------------------------------------------- /components/web_server/www/html/settingsReset.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /components/web_server/www/html/settingsSave.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /components/web_server/www/html/sysinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "ipAddress": "192.168.1.128", 3 | "dns": "192.168.1.1", 4 | "rss": -70, 5 | "wifiAuthFail" : false, 6 | "clients": [ 7 | { 8 | "ipAddress": "192.168.4.2", 9 | "macAddress": "4c:bb:58:f0:94:de" 10 | }, 11 | { 12 | "ipAddress": "192.168.4.2", 13 | "macAddress": "4c:bb:58:f0:94:de" 14 | }, 15 | { 16 | "ipAddress": "192.168.4.2", 17 | "macAddress": "4c:bb:58:f0:94:de" 18 | } 19 | ], 20 | "filterList": [ 21 | "4c:bb:58:f0:94:de", 22 | "4c:bb:58:f0:94:de", 23 | "4c:bb:58:f0:94:de" 24 | ], 25 | "filterListType": "Allow" 26 | } 27 | -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/dark.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/dark.css.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/error_404.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/error_404.html.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/functions.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/functions.js.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/index.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/index.html.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/info.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/info.html.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/info.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/info.js.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/main.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/main.css.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/scan.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/scan.js.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/settings.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/settings.html.gz -------------------------------------------------------------------------------- /components/web_server/www/output/gzip/settings.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/components/web_server/www/output/gzip/settings.js.gz -------------------------------------------------------------------------------- /components/wifi_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS wifi_event_handler.c wifi_init.c wifi_handler.c 2 | 3 | INCLUDE_DIRS include 4 | REQUIRES cmd_router cmd_nvs nvs_flash utils 5 | esp_netif esp_wifi wpa_supplicant esp_hw_support router_handler) -------------------------------------------------------------------------------- /components/wifi_handler/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | # This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 5 | # this will take the sources in the src/ directory, compile them and link them into 6 | # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, 7 | # please read the SDK documents if you need to do this. 8 | # 9 | 10 | COMPONENT_ADD_INCLUDEDIRS := . 11 | -------------------------------------------------------------------------------- /components/wifi_handler/include/wifi_event_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #define JOIN_TIMEOUT_MS (3000) 20 | 21 | extern EventGroupHandle_t wifi_event_group; 22 | extern int WIFI_CONNECTED_BIT; 23 | void wifi_events_register_init(void); 24 | extern bool is_scanning_progress; 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /components/wifi_handler/include/wifi_handler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | char *wifi_scan_handler(void); 17 | char *wifi_info_handler(void); 18 | extern bool has_static_ip; 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /components/wifi_handler/include/wifi_init.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | #include 17 | 18 | extern esp_netif_t *wifiAP; 19 | extern esp_netif_t *wifiSTA; 20 | void wifi_init(); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /components/wifi_handler/wifi_event_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "lwip/sockets.h" 21 | 22 | #include "router_globals.h" 23 | #include "initialization.h" 24 | #include "wifi_init.h" 25 | #include "wifi_event_handler.h" 26 | #include "router_handler.h" 27 | #include "wifi_handler.h" 28 | #include "mac_filter.h" 29 | 30 | static const char *TAG = "wifi_event_handler"; 31 | EventGroupHandle_t wifi_event_group; 32 | int WIFI_CONNECTED_BIT = BIT0; 33 | 34 | #define WIFI_RETRY_COUNT 3 35 | #define WIFI_RETRY_DELAY_MAX 8000 36 | static int wifi_retry_count = 0; 37 | 38 | void wifi_retry_handler(void) 39 | { 40 | if (wifi_retry_count < WIFI_RETRY_COUNT) 41 | { 42 | ESP_LOGI(TAG, "Retrying WiFi connection (%d/%d)", wifi_retry_count + 1, WIFI_RETRY_COUNT); 43 | esp_wifi_scan_stop(); 44 | if (!is_scanning_progress) 45 | esp_wifi_connect(); 46 | wifi_retry_count++; 47 | } 48 | else 49 | { 50 | vTaskDelay(WIFI_RETRY_DELAY_MAX / portTICK_PERIOD_MS); 51 | ESP_LOGW(TAG, "Maximum number of WiFi connection retries reached (%d)", WIFI_RETRY_COUNT); 52 | wifi_retry_count = 0; 53 | wifi_retry_handler(); 54 | } 55 | } 56 | static void wifi_disconnect_handler(const uint8_t reason) 57 | { 58 | switch (reason) 59 | { 60 | case WIFI_REASON_AUTH_EXPIRE: 61 | case WIFI_REASON_HANDSHAKE_TIMEOUT: 62 | case WIFI_REASON_AUTH_FAIL: 63 | case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: 64 | ESP_LOGI(TAG, "Authentication failed"); 65 | IsWifiAuthFail = true; 66 | wifi_retry_handler(); 67 | break; 68 | default: 69 | IsWifiAuthFail = false; 70 | wifi_retry_handler(); 71 | break; 72 | } 73 | ap_connect = false; 74 | } 75 | 76 | //----------------------------------------------------------------------------- 77 | // Function to set DNS server 78 | void set_dns_server(esp_netif_dns_info_t dnsIP) { 79 | // esp_netif_dhcp_status_t dhcp_status; 80 | esp_netif_dhcps_stop(wifiAP); // Stop DHCP server 81 | if (IsCustomDnsEnable || has_static_ip) { 82 | dnsIP.ip.u_addr.ip4.addr = ipaddr_addr(customDNSip); 83 | } 84 | ESP_ERROR_CHECK(esp_netif_set_dns_info(wifiAP, ESP_NETIF_DNS_MAIN, &dnsIP)); 85 | // Set DHCP option for DNS server 86 | uint8_t opt_val = 1; // OFFER DNS option 87 | ESP_ERROR_CHECK(esp_netif_dhcps_option(wifiAP, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &opt_val, sizeof(opt_val))); 88 | ESP_LOGI(TAG, "Set DNS to: " IPSTR, IP2STR(&(dnsIP.ip.u_addr.ip4))); 89 | ESP_ERROR_CHECK(esp_netif_dhcps_start(wifiAP)); // Restart DHCP server 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | // wifi event handler 94 | static void wifi_event_handler(void *arg, esp_event_base_t event_base, 95 | int32_t event_id, void *event_data) 96 | { 97 | esp_netif_dns_info_t dns; 98 | 99 | if (WIFI_EVENT == event_base && WIFI_EVENT_STA_START == event_id) 100 | { 101 | esp_wifi_connect(); 102 | } 103 | else if (WIFI_EVENT == event_base && WIFI_EVENT_STA_DISCONNECTED == event_id) 104 | { 105 | wifi_event_sta_disconnected_t *disconnected = (wifi_event_sta_disconnected_t *)event_data; 106 | xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); 107 | 108 | ESP_LOGI(TAG, "WiFi disconnected"); 109 | wifi_disconnect_handler(disconnected->reason); 110 | } 111 | else if (IP_EVENT == event_base && IP_EVENT_STA_GOT_IP == event_id) 112 | { 113 | ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; 114 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 115 | ap_connect = true; 116 | my_ip = event->ip_info.ip.addr; 117 | delete_portmap_tab(); 118 | apply_portmap_tab(); 119 | 120 | if (esp_netif_get_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns) == ESP_OK) 121 | { 122 | set_dns_server(dns); 123 | } 124 | xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); 125 | } 126 | else if (WIFI_EVENT == event_base && WIFI_EVENT_AP_STACONNECTED == event_id) 127 | { 128 | 129 | wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; 130 | mac_filter(event->mac, event->aid); 131 | connect_count++; 132 | ESP_LOGI(TAG, "%d. station connected", connect_count); 133 | } 134 | else if (WIFI_EVENT == event_base && WIFI_EVENT_AP_STADISCONNECTED == event_id) 135 | { 136 | if (!(connect_count <= 0)) 137 | connect_count--; 138 | ESP_LOGI(TAG, "station disconnected - %d remain", connect_count); 139 | } 140 | } 141 | 142 | //----------------------------------------------------------------------------- 143 | // initiating wifi event registry 144 | void wifi_events_register_init(void) 145 | { 146 | esp_event_handler_instance_t instance_any_id; 147 | esp_event_handler_instance_t instance_got_ip; 148 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 149 | ESP_EVENT_ANY_ID, 150 | &wifi_event_handler, 151 | NULL, 152 | &instance_any_id)); 153 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 154 | IP_EVENT_STA_GOT_IP, 155 | &wifi_event_handler, 156 | NULL, 157 | &instance_got_ip)); 158 | } 159 | -------------------------------------------------------------------------------- /components/wifi_handler/wifi_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "router_globals.h" 16 | #include "initialization.h" 17 | #include "wifi_handler.h" 18 | #include "wifi_event_handler.h" 19 | #include "mac_filter.h" 20 | #include "esp_mac.h" 21 | #include "esp_wifi_ap_get_sta_list.h" 22 | 23 | bool is_scanning_progress = false; 24 | //----------------------------------------------------------------------------- 25 | // Handles wifi scan and return AP records in json string format 26 | char* IRAM_ATTR wifi_scan_handler(void) 27 | { 28 | // Disconnect from the current access point if not already connected 29 | if (!ap_connect) 30 | { 31 | is_scanning_progress = true; 32 | esp_wifi_disconnect(); 33 | vTaskDelay(500 / portTICK_PERIOD_MS); 34 | } 35 | 36 | wifi_scan_config_t scan_config = { 37 | .ssid = NULL, 38 | .bssid = NULL, 39 | .channel = 0, 40 | .scan_type = WIFI_SCAN_TYPE_ACTIVE, 41 | }; 42 | printf("Start scanning...\n"); 43 | esp_err_t err = esp_wifi_scan_start(&scan_config, true); 44 | if (err == ESP_OK) 45 | { 46 | printf("scanning completed!\n"); 47 | uint16_t ap_num; 48 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_num)); 49 | wifi_ap_record_t *ap_records = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * 100); 50 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records)); 51 | printf("Found %d access points:\n", ap_num); 52 | cJSON *root = cJSON_CreateObject(); 53 | cJSON *aps = cJSON_CreateArray(); 54 | cJSON_AddItemToObject(root, "aps", aps); 55 | char bssid_str[18]; 56 | for (int i = 0; i < ap_num; i++) 57 | { 58 | sprintf(bssid_str, MACSTR, MAC2STR(ap_records[i].bssid)); 59 | cJSON *ap = cJSON_CreateObject(); 60 | cJSON_AddNumberToObject(ap, "c", ap_records[i].primary); 61 | cJSON_AddStringToObject(ap, "m", (const char *)bssid_str); 62 | cJSON_AddStringToObject(ap, "ss", (const char *)ap_records[i].ssid); 63 | cJSON_AddNumberToObject(ap, "r", ap_records[i].rssi); 64 | cJSON_AddNumberToObject(ap, "e", ap_records[i].authmode); 65 | cJSON_AddItemToArray(aps, ap); 66 | } 67 | char *my_json_string = cJSON_Print(root); 68 | cJSON_Delete(root); 69 | free(ap_records); 70 | // Reconnect to the access point if not already connected 71 | if (!ap_connect) 72 | { 73 | is_scanning_progress = false; 74 | vTaskDelay(300 / portTICK_PERIOD_MS); 75 | esp_wifi_connect(); 76 | } 77 | return my_json_string; 78 | } 79 | printf("scanning Failed!\n"); 80 | return "false"; 81 | } 82 | 83 | //----------------------------------------------------------------------------- 84 | // Handles wifi information and return in json format 85 | char* IRAM_ATTR wifi_info_handler(void) 86 | { 87 | wifi_ap_record_t ap_info; 88 | wifi_sta_list_t wifi_sta_list; 89 | wifi_sta_mac_ip_list_t adapter_sta_list; 90 | esp_netif_ip_info_t ip_info; 91 | esp_netif_dns_info_t dns_info; 92 | char *ssid = ""; 93 | int8_t rssi = 0; 94 | char gateway_address[32]; 95 | char ip_address[32]; 96 | char dns[32]; 97 | 98 | if (ap_connect) { 99 | memset(&ap_info, 0, sizeof(ap_info)); 100 | memset(&ip_info, 0, sizeof(ip_info)); 101 | if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) { 102 | ssid = (char *)ap_info.ssid; 103 | rssi = ap_info.rssi; 104 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 105 | ESP_ERROR_CHECK(esp_netif_get_dns_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), ESP_NETIF_DNS_MAIN, &dns_info)); 106 | 107 | strlcpy(gateway_address, ip4addr_ntoa((const ip4_addr_t *)&ip_info.gw), sizeof(gateway_address)); 108 | strlcpy(ip_address, ip4addr_ntoa((const ip4_addr_t *)&ip_info.ip), sizeof(ip_address)); 109 | strlcpy(dns, ip4addr_ntoa((const ip4_addr_t *)&dns_info.ip), sizeof(dns)); 110 | } else { 111 | ssid = ""; 112 | rssi = 0; 113 | strcpy(gateway_address, ""); 114 | strcpy(ip_address, ""); 115 | strcpy(dns, ""); 116 | } 117 | } 118 | memset(&wifi_sta_list, 0, sizeof(wifi_sta_list)); 119 | memset(&adapter_sta_list, 0, sizeof(adapter_sta_list)); 120 | ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list(&wifi_sta_list)); 121 | ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list_with_ip(&wifi_sta_list, &adapter_sta_list)); 122 | 123 | cJSON *root = cJSON_CreateObject(); 124 | cJSON_AddStringToObject(root, "ssid", ssid); 125 | cJSON_AddStringToObject(root, "gatewayAddress", gateway_address); 126 | cJSON_AddStringToObject(root, "ipAddress", ip_address); 127 | cJSON_AddStringToObject(root, "dns", (has_static_ip || IsCustomDnsEnable) ? customDNSip : dns); 128 | cJSON_AddStringToObject(root, "filterListType", (IsAllowList ? "Allow" : "Deny")); 129 | cJSON_AddNumberToObject(root, "rss", rssi); 130 | cJSON_AddBoolToObject(root, "wifiAuthFail", IsWifiAuthFail); 131 | cJSON *clients = cJSON_AddArrayToObject(root, "clients"); 132 | cJSON *json = retrieve_mac_addresses_as_json(); 133 | 134 | // Iterate over the connected stations 135 | for (int i = 0; i < wifi_sta_list.num; i++) { 136 | esp_netif_pair_mac_ip_t station = adapter_sta_list.sta[i]; 137 | cJSON *client = cJSON_CreateObject(); 138 | cJSON_AddStringToObject(client, "ipAddress", ip4addr_ntoa((const ip4_addr_t *)&(station.ip))); 139 | char mac_address[18]; 140 | sprintf(mac_address, MACSTR, MAC2STR(station.mac)); 141 | cJSON_AddStringToObject(client, "macAddress", mac_address); 142 | cJSON_AddItemToArray(clients, client); 143 | 144 | } 145 | 146 | if (json != NULL) { 147 | cJSON_AddItemToObject(root, "filterList", json); 148 | } 149 | char *my_json_string = cJSON_Print(root); 150 | cJSON_Delete(root); 151 | return my_json_string; 152 | } 153 | 154 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /components/wifi_handler/wifi_init.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jaya Satish 3 | * 4 | *@copyright Copyright (c) 2023 5 | *Licensed under MIT 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "lwip/ip4_addr.h" 19 | #include "dhcpserver/dhcpserver.h" 20 | 21 | #include "router_globals.h" 22 | #include "initialization.h" 23 | #include "wifi_init.h" 24 | #include "wifi_event_handler.h" 25 | #include "router_handler.h" 26 | #include "mac_generator.h" 27 | 28 | static const char *TAG = "wifi_handler"; 29 | 30 | esp_netif_t *wifiAP; 31 | esp_netif_t *wifiSTA; 32 | 33 | bool has_static_ip = false; 34 | 35 | //----------------------------------------------------------------------------- 36 | // initiating wifi setup 37 | void wifi_init() 38 | { 39 | esp_log_level_set("wifi", ESP_LOG_NONE); 40 | wifi_event_group = xEventGroupCreate(); 41 | esp_netif_init(); 42 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 43 | wifiAP = esp_netif_create_default_wifi_ap(); 44 | wifiSTA = esp_netif_create_default_wifi_sta(); 45 | 46 | esp_netif_ip_info_t ipInfo_sta; 47 | if ((strlen(ssid) > 0) && (strlen(static_ip) > 0) && (strlen(subnet_mask) > 0) && (strlen(gateway_addr) > 0)) { 48 | has_static_ip = true; 49 | my_ip = ipInfo_sta.ip.addr = ipaddr_addr(static_ip); 50 | ipInfo_sta.gw.addr = ipaddr_addr(gateway_addr); 51 | ipInfo_sta.netmask.addr = ipaddr_addr(subnet_mask); 52 | esp_netif_dhcpc_stop(wifiSTA); // Don't run a DHCP client 53 | esp_netif_set_ip_info(wifiSTA, &ipInfo_sta); 54 | apply_portmap_tab(); 55 | } 56 | 57 | my_ap_ip = ipaddr_addr(ap_ip); 58 | esp_netif_ip_info_t ipInfo_ap; 59 | ipInfo_ap.ip.addr = my_ap_ip; 60 | ipInfo_ap.gw.addr = my_ap_ip; 61 | IP4_ADDR(&ipInfo_ap.netmask, 255, 255, 255, 0); 62 | esp_netif_dhcps_stop(wifiAP); // Stop before setting IP WifiAP 63 | esp_netif_set_ip_info(wifiAP, &ipInfo_ap); 64 | esp_netif_dhcps_start(wifiAP); 65 | 66 | wifi_events_register_init(); 67 | custom_mac_generator(); 68 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 69 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 70 | ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); 71 | ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); 72 | 73 | /* ESP WIFI CONFIG */ 74 | wifi_config_t wifi_config = {0}; 75 | wifi_config_t ap_config = { 76 | .ap = { 77 | .authmode = WIFI_AUTH_WPA2_PSK, 78 | .ssid_hidden = 0, 79 | .max_connection = 10, 80 | .beacon_interval = 100, 81 | .pairwise_cipher = WIFI_CIPHER_TYPE_CCMP 82 | } 83 | }; 84 | 85 | strlcpy((char *)ap_config.ap.ssid, ap_ssid, sizeof(ap_config.ap.ssid)); 86 | if (strlen(ap_passwd) < 8) { 87 | ap_config.ap.authmode = WIFI_AUTH_OPEN; 88 | } else { 89 | strlcpy((char *)ap_config.ap.password, ap_passwd, sizeof(ap_config.ap.password)); 90 | } 91 | 92 | if (strlen(ssid) > 0) { 93 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); 94 | 95 | // Set SSID 96 | strlcpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); 97 | // Set password 98 | if (strlen(ent_username) == 0) { 99 | ESP_LOGI(TAG, "STA regular connection"); 100 | strlcpy((char *)wifi_config.sta.password, passwd, sizeof(wifi_config.sta.password)); 101 | } 102 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); 103 | if (strlen(ent_username) != 0 && strlen(ent_identity) != 0) { 104 | ESP_LOGI(TAG, "STA enterprise connection"); 105 | if (strlen(ent_identity) != 0) { 106 | esp_eap_client_set_identity((uint8_t *)ent_identity, strlen(ent_identity)); // Provide identity 107 | } else { 108 | esp_eap_client_set_identity((uint8_t *)ent_username, strlen(ent_username)); // Fallback to username 109 | } 110 | esp_eap_client_set_username((uint8_t *)ent_username, strlen(ent_username)); // Provide username 111 | esp_eap_client_set_password((uint8_t *)passwd, strlen(passwd)); // Provide password 112 | ESP_ERROR_CHECK(esp_wifi_sta_enterprise_enable()); 113 | } 114 | 115 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_config)); 116 | } else { 117 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); 118 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_config)); 119 | } 120 | 121 | // Enable DNS (offer) for DHCP server 122 | uint8_t dhcps_dns_value = 1; // OFFER DNS option 123 | ESP_ERROR_CHECK(esp_netif_dhcps_option(wifiAP, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value))); 124 | 125 | ESP_ERROR_CHECK(esp_wifi_start()); 126 | 127 | if (strlen(ssid) > 0) { 128 | ESP_LOGI(TAG, "wifi_init_apsta finished."); 129 | ESP_LOGI(TAG, "connect to ap SSID: %s ", ssid); 130 | } else { 131 | ESP_LOGI(TAG, "wifi_init_ap with default finished."); 132 | } 133 | xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS); 134 | } 135 | 136 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /docs/Build_setup.md: -------------------------------------------------------------------------------- 1 | # ESP32-NAT Router + 2 | 3 | ## How to setup environment and build project 4 | 5 | ***Just a friendly reminder that building firmware for the ESP32-NAT Router + can be a bit challenging, so it's important to follow the documentation closely. However, as you dive deeper into the project, you'll discover and learn more interesting features to enjoy. Thank you for your interest in this project, and happy building!*** 6 | 7 | ##### The software and technologies used in this project are varied and include but are not limited to: 8 | 9 | 1. Embedded C for programming the ESP32 microcontroller 10 | 2. Python for scripting and automation purposes 11 | 3. Ruby/jekyll for web development 12 | 4. HTML/CSS/JS for building the user interface of the router 13 | 5. Node.js runtime environment for creating `auto_generate.exe` for windows. 14 | 6. Bash/Bat scripting for executing various tasks and scripts 15 | 16 | 17 | ### Prerequisites 18 | 19 | Before diving into the project, there are several prerequisites that need to be installed: 20 | 21 | ***Note : Currently Supporting upto espidf 4.x.x and PltformIO : `PLATFORM: Espressif 32 (5.x.x)`*** 22 | 23 | - [VisualStudio Code](https://code.visualstudio.com/) 24 | - [PlatformIO IDE for VSCode](https://platformio.org/install/ide?install=vscode) 25 | - [Standard setup toolchain](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#get-started-get-prerequisites) for your platform as outlined in the Espressif documentation 26 | 27 | If you plan to run a web server or build web interfaces, you will also need to install: 28 | 29 | - [Ruby](https://www.ruby-lang.org/) 30 | - [Jekyll](https://jekyllrb.com/) 31 | 32 | ### Get Started 33 | 34 | To clone this repository to your desired location, follow these steps: 35 | 36 | - Navigate to your desired location using the command line interface using `cd` command. 37 | 38 | - Run the command : 39 | 40 | `git clone https://github.com/gjroots/esp32_nat_router_plus.git` 41 | 42 | - Once the repository has been cloned, you can open the project in Visual Studio Code and click on the "Start" button provided by PlatformIO. 43 | 44 | - Please note that this may take some time as PlatformIO will download all the tools required for development. 45 | 46 | - Make your desired changes to the code and then click on "Compile and Upload" to upload your changes to the board. 47 | 48 | *Until this stage, the instructions are common to any project when building firmware. However, if you want to make changes to the web interface, follow these steps:* 49 | 50 | ##### This project includes two Python scripts which offer special functionality through PlatformIO. 51 | 52 | - `pre_extra_script.py` manages all setup to be up to date, gzips all built web interface files, updates the version, checks for any changes in the web interface, and auto builds, among other things. 53 | - `post_extra_script.py` manages everything after building the firmware, such as copying .bin files to the release folder, merging all bin's to a single bin file, and zipping all firmware files. 54 | 55 | These scripts are an important part of the project to automate some tasks to simple, and providing essential functionality to streamline the build process. 56 | 57 | ### WWW Folder 58 | 59 | The `components/web_server/www` folder contains all the static files required for project's website. The `www/html` folder is the main folder within the `www` directory, where all the necessary site files are present 60 | 61 | ##### In our web interface building , we primarily use two folders: 62 | 63 | `components/web_server/www/html` and `components/web_server/www/output` . Once we have completed building the site, the files will appear in the `components/web_server/www/output/html` folder. To optimize the memory usage and increase the speed of site loading, we have converted all of the building site files into *gzip* files in`components/web_server/www/output/gzip` folder. This helps to reduce the overall size of the files and ensure that the site loads quickly and efficiently. 64 | 65 | ### What is Jekyll? 66 | 67 | [Jekyll](https://jekyllrb.com/) is a pre-compiler for static content generation that simplifies website maintenance. It uses Ruby to generate static HTML pages, which means that the outputted HTML is the same as if it were written by hand. 68 | 69 | To start using Jekyll, you need to follow these steps: 70 | 71 | - Install Ruby, Gem package manager & Jekyll ( check [here](https://www.ruby-lang.org/ ) ) 72 | 73 | - Run `bundle install` in the `components/web_server/www/html` folder to install dependencies 74 | 75 | #### How to run a local server 76 | 77 | Once Jekyll is installed, you can start a local server by following these steps: 78 | 79 | - Run `bundle exec jekyll serve --watch` in the `components/web_server/www/html` folder 80 | 81 | - Navigate to `http://127.0.0.1:4000` to see the website 82 | 83 | - Changes made to files will be reflected in real-time, so you can test and make adjustments to your site's appearance and functionality. (make sure to refresh the page though) 84 | 85 | - Alternatively, You can run the server, navigate to the root folder of the project and use `./start_web_server.sh` for Linux terminal or `start_web_server.bat` for Windows. 86 | 87 | ### How to update web-server files? 88 | 89 | #### Build 90 | 91 | - Building is a mandatory step after making changes. you can run the command `JEKYLL_ENV=production bundle exec jekyll build` for Linux Terminal  OR 92 | 93 |    `set JEKYLL_ENV=production bundle exec jekyll build`  for Windows CMD  in the `components/web_server/www/html` folder to generate and return to production mode minified files. 94 | 95 | - Alternatively, navigate to the root folder of the project and use `./build_web_pages.sh` in Linux terminal or `build_web_pages.bat` in Windows CMD. 96 | 97 | #### Auto Mode 98 | 99 | During the Auto Mode, the Python script monitors the `www/output/html` folder for any changes and automatically triggers the site build process and files compress into the `www/output/gzip` folder. 100 | 101 | Note: This process is only triggered when changes are made through the live server or run build manually, as Jekyll has two modes, namely the **development mode** and the **production mode**. After completing the development phase, the site is converted to production mode, where in the files are minified to enhance the site's loading speed. 102 | 103 | ##### Alternatively (Windows only) 104 | 105 | - Make sure you have Jekyll & Gems installed (see above) 106 | 107 | - Launch `auto_generate.exe` present in `www` folder 108 | 109 | - Wait for it to finish 110 | 111 | - That's it **¯\_(ツ)_/¯** 112 | 113 | **Note: These building steps are only necessary when changes have been made to the site files. Otherwise, these steps are not required.** 114 | 115 | ## Credit's 116 | 117 |  [@samdenty](https://github.com/samdenty)/**[Wi-PWN](https://github.com/samdenty/Wi-PWN)** 118 | 119 | The UI code and concepts used in this project were developed by Sam Denty in the Wi-PWN project. By utilizing this user interface design and adapting it to the ESP32 NAT Router +, we are able to offer a user-friendly and intuitive interface to users. Thanks to Sam Denty's work, To provide a modern and responsive web interface. 120 | 121 | ##### Auto generate sample view 122 | ![autogenerate.exe](images/auto_generate.png) -------------------------------------------------------------------------------- /docs/images/auto_generate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/auto_generate.png -------------------------------------------------------------------------------- /docs/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/info.png -------------------------------------------------------------------------------- /docs/images/scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/scan.png -------------------------------------------------------------------------------- /docs/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/settings.png -------------------------------------------------------------------------------- /docs/images/win_flash_full_bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/win_flash_full_bin.png -------------------------------------------------------------------------------- /docs/images/win_flash_multi_bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gjroots/esp32_nat_router_plus/c3f18851b961019fb6bf7a0154d9b646a2a40cb4/docs/images/win_flash_multi_bin.png -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # ESP-IDF Partition Table 2 | # Name, Type, SubType, Offset, Size, Flags 3 | nvs, data, nvs, 0x9000, 0x4000, 4 | otadata, data, ota, 0xd000, 0x2000, 5 | phy_init, data, phy, 0xf000, 0x1000, 6 | storage, data, fat, 0x10000, 0x90000, 7 | ota_0, app, ota_0, 0x100000, 0x140000, 8 | ota_1, app, ota_1, 0x240000, 0x140000, 9 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [extra_script_settings] 13 | version = 1.2.0 ; Set the version to the project 14 | merge_to_single_bin = 1 ; (0 = disable) Enable the conversion of a multiple bins to single bin file of 0x0 format 15 | 16 | 17 | [env] ; global files, common to all environments 18 | framework = espidf 19 | platform = espressif32 20 | monitor_raw = yes 21 | monitor_speed = 115200 22 | board_build.partitions = partitions.csv 23 | board_build.embed_txtfiles = 24 | components/web_server/www/output/gzip/index.html.gz 25 | components/web_server/www/output/gzip/settings.html.gz 26 | components/web_server/www/output/gzip/info.html.gz 27 | components/web_server/www/output/gzip/error_404.html.gz 28 | components/web_server/www/output/gzip/main.css.gz 29 | components/web_server/www/output/gzip/dark.css.gz 30 | 31 | components/web_server/www/output/gzip/scan.js.gz 32 | components/web_server/www/output/gzip/info.js.gz 33 | components/web_server/www/output/gzip/settings.js.gz 34 | components/web_server/www/output/gzip/functions.js.gz 35 | 36 | 37 | [env:esp32dev] 38 | board = esp32dev 39 | extra_scripts = 40 | pre:pre_extra_script.py 41 | post:post_extra_script.py -------------------------------------------------------------------------------- /post_extra_script.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os 3 | import zipfile 4 | 5 | 6 | Import("env") 7 | 8 | config = env.GetProjectConfig() 9 | version = config.get("extra_script_settings", "version") 10 | 11 | merge_to_single_bin = int(config.get( 12 | "extra_script_settings", "merge_to_single_bin")) 13 | 14 | 15 | def copy_files(): 16 | os.makedirs("release", exist_ok=True) 17 | files = [ 18 | ('.pio/build/esp32dev/firmware.bin', 19 | f'esp32nat_Router+_v{version}.bin'), 20 | ('.pio/build/esp32dev/bootloader.bin', 'bootloader.bin'), 21 | ('.pio/build/esp32dev/partitions.bin', 'partitions.bin') 22 | ] 23 | for src, dest in files: 24 | shutil.copyfile(src, f'release/{dest}') 25 | print("==========> Bin files are copied to the /release folder.\n") 26 | 27 | 28 | def convert_single_bin(): 29 | build_dir = env.subst("$BUILD_DIR") 30 | binary_path = os.path.join(build_dir, "firmware.bin") 31 | bootloader_path = os.path.join(build_dir, "bootloader.bin") 32 | partitions_path = os.path.join(build_dir, "partitions.bin") 33 | release_path = os.path.join(env.subst("$PROJECT_DIR"), "release") 34 | merged_file = os.path.join(release_path, f"esp32nat_Router+_full_v{version}_0x0.bin") 35 | esptool_dir = env.PioPlatform().get_package_dir("tool-esptoolpy") 36 | esptool_command = f'python {str(esptool_dir)}\\esptool.py --chip esp32 merge_bin -o {merged_file} --flash_freq 40m --flash_size 4MB 0x1000 {bootloader_path} 0x8000 {partitions_path} 0x10000 {binary_path}' 37 | 38 | os.makedirs("release", exist_ok=True) 39 | env.Execute(esptool_command) 40 | zip_all_bins() 41 | 42 | 43 | def zip_all_bins(): 44 | files = [ 45 | (f'release/esp32nat_Router+_v{version}.bin', 46 | f'esp32nat_Router+_v{version}.bin'), 47 | ('release/partitions.bin', 'partitions.bin'), 48 | ('release/bootloader.bin', 'bootloader.bin') 49 | ] 50 | with zipfile.ZipFile(f'release/esp32nat_Router+_v{version}.zip', 'w', zipfile.ZIP_DEFLATED) as zipObj: 51 | for file in files: 52 | zipObj.write(*file) 53 | 54 | with zipfile.ZipFile(f'release/esp32nat_Router+_full_v{version}_0x0.zip', 'w', zipfile.ZIP_DEFLATED) as zipObj: 55 | zipObj.write( 56 | f'release/esp32nat_Router+_full_v{version}_0x0.bin', f'esp32nat_Router+_full_v{version}_0x0.bin') 57 | 58 | print("\n==========> zip files created successfully") 59 | 60 | 61 | def after_build_program(source, target, env): 62 | print("\n==========> Building Successful\n") 63 | copy_files() 64 | if merge_to_single_bin == 1: 65 | convert_single_bin() 66 | 67 | 68 | env.AddPostAction("buildprog", after_build_program) 69 | -------------------------------------------------------------------------------- /pre_extra_script.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import os 4 | import gzip 5 | import re 6 | import platform 7 | 8 | print("\033[94m╔═╗┌─┐┌─┐ ╔╗╔┌─┐┌┬┐ ╦═╗┌─┐┬ ┬┌┬┐┌─┐┬─┐ _|_\n║╣ └─┐├─┘32─║║║├─┤ │ ╠╦╝│ ││ │ │ ├┤ ├┬┘ | \n╚═╝└─┘┴ ╝╚╝┴ ┴ ┴ ╩╚═└─┘└─┘ ┴ └─┘┴└─\033[0m\n") 9 | 10 | Import("env") 11 | 12 | 13 | os.environ['PYTHONDONTWRITEBYTECODE'] = '1' 14 | config = env.GetProjectConfig() 15 | version = config.get("extra_script_settings", "version") 16 | 17 | jekyll_install_status = False 18 | system_platform = platform.system() 19 | 20 | if os.system('jekyll -v') == 0: 21 | # print('Jekyll is installed on your system.') 22 | jekyll_install_status = True 23 | else: 24 | print( 25 | '\033[91mJekyll is not installed on your system. Please install Jekyll.\033[0m') 26 | 27 | if system_platform =="Linux": 28 | # for linux user 29 | def build_site(): 30 | env.Execute("build_web_pages.sh") 31 | elif system_platform == "Windows": 32 | # for windows user 33 | def build_site(): 34 | env.Execute("build_web_pages.bat") 35 | 36 | 37 | base_folder_path = './components/web_server/www/' 38 | 39 | # check version in function.js file and update new version 40 | def version_check(): 41 | pattern = r'version\s*=\s*"([\d.]+(?:_\w+)?)"' 42 | # Define the new version number 43 | new_version = version 44 | filename = base_folder_path + 'html/js/functions.js' 45 | 46 | # Open the JavaScript file 47 | with open(filename, 'r+', encoding='utf-8') as js_file: 48 | content = js_file.read() 49 | if re.search(pattern, content): 50 | old_version = re.search(pattern, content).group(1) 51 | if old_version == new_version: 52 | # print('Version already up to date.') 53 | return 54 | else: 55 | new_content = re.sub(pattern, 'version="' + new_version + '"', content) 56 | js_file.seek(0) 57 | js_file.write(new_content) 58 | js_file.truncate() 59 | print(f'New version released: {new_version}') 60 | else: 61 | print('Version not found.') 62 | return 63 | # Try to remove the temporary file 64 | try: 65 | os.remove(filename + '.temp') 66 | except FileNotFoundError: 67 | pass 68 | 69 | # gzip web pages and move to output/gzip folder 70 | def gzip_web_pages(): 71 | dirpath = base_folder_path + 'output/html/' 72 | output_dir = base_folder_path + 'output/gzip' 73 | if not os.path.exists(output_dir): 74 | os.makedirs(output_dir) 75 | for subdir, dirs, files in os.walk(dirpath): 76 | for filename in files: 77 | file_path = subdir + os.sep + filename 78 | if file_path.endswith(".html") or file_path.endswith(".css") or file_path.endswith(".js"): 79 | new_filename = filename 80 | if filename == "404.html": 81 | new_filename = "error_404.html" 82 | with open(file_path, 'rb') as f_in: 83 | with gzip.open(os.path.join(output_dir, new_filename + '.gz'), 'wb') as f_out: 84 | f_out.writelines(f_in) 85 | 86 | 87 | def files_modtime(folder_path): 88 | # Create a dictionary to store the modification times of each file 89 | file_modtimes = {} 90 | try: 91 | for root, dirs, files in os.walk(folder_path): 92 | for file in files: 93 | file_path = os.path.join(root, file) 94 | file_modtime = os.path.getmtime(file_path) 95 | file_modtimes[file_path] = file_modtime 96 | return file_modtimes 97 | except FileNotFoundError: 98 | return file_modtimes 99 | 100 | 101 | def read_json_file(file_path): 102 | try: 103 | with open(file_path, 'r') as file: 104 | data = json.load(file) 105 | return data 106 | except FileNotFoundError: 107 | return {"modtimes": ""} 108 | 109 | 110 | def write_json_file(file_path, data): 111 | with open(file_path, 'w') as file: 112 | json.dump(data, file) 113 | 114 | 115 | # check files modified or not 116 | def file_changes_check(): 117 | # Call the function once to get the initial modification times 118 | original_modtimes = files_modtime( 119 | base_folder_path + 'output/html/') 120 | modtime_json_file_path = '.pio/modtime_data.json' 121 | 122 | data = read_json_file(modtime_json_file_path) 123 | 124 | if data['modtimes'] != original_modtimes: 125 | print("\033[91mFiles have been changed.\033[0m\n") 126 | gzip_web_pages() 127 | data['modtimes'] = original_modtimes 128 | write_json_file(modtime_json_file_path, data) 129 | # else: 130 | # print("The files have not been changed.\n") 131 | 132 | #checks website in development or production 133 | def is_website_production_build(): 134 | try: 135 | file_path = base_folder_path + "output/html/js/functions.js" 136 | folder_path = base_folder_path + "output/gzip/" 137 | word = "data/restart.json" 138 | with open(file_path, 'r', encoding="utf-8") as file: 139 | data = file.read() 140 | if (not word in data): 141 | print("\033[91mWebsite is in development\033[0m") 142 | build_site() 143 | # else: 144 | # print("Web site is in production") 145 | 146 | if not os.listdir(folder_path): 147 | gzip_web_pages() 148 | except FileNotFoundError: 149 | build_site() 150 | 151 | 152 | version_check() 153 | if jekyll_install_status: 154 | is_website_production_build() 155 | file_changes_check() 156 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS esp32_nat_router.c 2 | INCLUDE_DIRS include) 3 | 4 | 5 | 6 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/index.html.gz" TEXT) 7 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/settings.html.gz" TEXT) 8 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/info.html.gz" TEXT) 9 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/error_404.html.gz" TEXT) 10 | 11 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/main.css.gz" TEXT) 12 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/dark.css.gz" TEXT) 13 | 14 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/scan.js.gz" TEXT) 15 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/settings.js.gz" TEXT) 16 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/info.js.gz" TEXT) 17 | target_add_binary_data(${COMPONENT_TARGET} "../components/web_server/www/output/gzip/functions.js.gz" TEXT) -------------------------------------------------------------------------------- /src/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Example Configuration" 2 | 3 | config STORE_HISTORY 4 | bool "Store command history in flash" 5 | default y 6 | help 7 | Linenoise line editing library provides functions to save and load 8 | command history. If this option is enabled, initalizes a FAT filesystem 9 | and uses it to store command history. 10 | 11 | endmenu -------------------------------------------------------------------------------- /src/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | 6 | -------------------------------------------------------------------------------- /src/esp32_nat_router.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "cmd_decl.h" 7 | #include "router_globals.h" 8 | #include "get_data_handler.h" 9 | #include "auth_handler.h" 10 | #include "initialization.h" 11 | #include "hardware_handler.h" 12 | #include "web_server.h" 13 | #include "console_handler.h" 14 | #include "file_system.h" 15 | #include "mac_generator.h" 16 | #include "nvm.h" 17 | #include "router_handler.h" 18 | #include "wifi_init.h" 19 | #include "ota_handler.h" 20 | 21 | #if !IP_NAPT 22 | #error "IP_NAPT must be defined" 23 | #endif 24 | 25 | /* Global vars */ 26 | uint16_t connect_count = 0; 27 | bool ap_connect = false; 28 | 29 | uint32_t my_ip; 30 | uint32_t my_ap_ip; 31 | 32 | static const char *TAG = "ESP32 NAT router +"; 33 | 34 | 35 | //----------------------------------------------------------------------------- 36 | void app_main(void) 37 | { 38 | initialize_nvs(); 39 | 40 | #if CONFIG_STORE_HISTORY 41 | initialize_filesystem(); 42 | ESP_LOGI(TAG, "Command history enabled"); 43 | #else 44 | ESP_LOGI(TAG, "Command history disabled"); 45 | #endif 46 | 47 | ESP_ERROR_CHECK(parms_init()); 48 | hardware_init(); 49 | get_portmap_tab(); 50 | wifi_init(); 51 | ip_napt_enable(my_ap_ip, 1); 52 | ESP_LOGI(TAG, "NAT is enabled"); 53 | 54 | if (IsWebServerEnable) 55 | { 56 | ESP_LOGI(TAG, "Starting config web server"); 57 | server = start_webserver(); 58 | } 59 | 60 | ota_update_init(); 61 | initialize_console(); 62 | register_system(); 63 | register_nvs(); 64 | register_router(); 65 | start_console(); 66 | } 67 | 68 | //----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /src/include/cmd_decl.h: -------------------------------------------------------------------------------- 1 | /* Declarations of command registration functions. 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | #pragma once 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include "cmd_system.h" 16 | #include "cmd_nvs.h" 17 | #include "cmd_router.h" 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /start_web_server.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd components\web_server\www\html 3 | echo Current directory: %cd% 4 | echo Server Started 5 | 6 | :: this command execute server on port 4000 7 | bundle exec jekyll serve --watch -------------------------------------------------------------------------------- /start_web_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd components\\web_server\\www\\html 3 | echo server started 4 | 5 | # this command execute server on port 4000 6 | bundle exec jekyll serve --watch 7 | 8 | --------------------------------------------------------------------------------