├── .github └── workflows │ ├── release.yaml │ └── shrinkHTML.py ├── .gitignore ├── CMakeLists.txt ├── README.md ├── components ├── cmd_nvs │ ├── CMakeLists.txt │ ├── cmd_nvs.c │ └── cmd_nvs.h ├── cmd_router │ ├── CMakeLists.txt │ ├── cmd_router.c │ ├── cmd_router.h │ └── router_globals.h └── cmd_system │ ├── CMakeLists.txt │ ├── cmd_system.c │ └── cmd_system.h ├── docs ├── BUILD.md ├── advanced.md ├── advanced.png ├── connected_clients.png ├── enterprise_wifi.png ├── index.png ├── lock.png ├── ota.md ├── ota.png ├── ota_custom.png ├── otalog.png ├── pio_compile.png ├── pio_open.png ├── pio_start.png ├── portmap.png ├── reset.png ├── scan.png ├── unlock.png ├── win_flash.png └── win_flash_full.png ├── larger.csv ├── platformio.ini ├── refresh_styles ├── sdkconfig.esp32 ├── sdkconfig.esp32-c3 ├── sdkconfig.esp32-c6 ├── sdkconfig.esp32-s2 ├── sdkconfig.esp32-s3 ├── src ├── CMakeLists.txt ├── cmd_decl.h ├── dnserver.c ├── esp32_nat_router.c ├── http_server.c ├── pages │ ├── about.html │ ├── advanced.html │ ├── apply.html │ ├── clients.html │ ├── config.html │ ├── favicon.ico │ ├── jquery-8a1045d9cbf50b52a0805c111ba08e94.js │ ├── lock.html │ ├── ota.html │ ├── otalog.html │ ├── portmap_end.html │ ├── portmap_start.html │ ├── reset.html │ ├── result.html │ ├── scan.html │ ├── styles-67aa3b0203355627b525be2ea57be7bf.css │ ├── styles_normal.css │ └── unlock.html ├── scan.c ├── scan.h ├── timer.c ├── timer.h └── urihandler │ ├── abouthandler.c │ ├── advancedhandler.c │ ├── applyhandler.c │ ├── clientshandler.c │ ├── handler.h │ ├── helper.c │ ├── helper.h │ ├── indexhandler.c │ ├── lockhandler.c │ ├── otahandler.c │ ├── portmaphandler.c │ ├── resthandler.c │ ├── resulthandler.c │ ├── scanhandler.c │ └── statichandler.c └── version /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Build Releases 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | contents: write 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/cache@v3 15 | with: 16 | path: | 17 | ~/.cache/pip 18 | ~/.platformio/.cache 19 | key: ${{ runner.os }}-pio 20 | - uses: actions/setup-python@v4 21 | with: 22 | python-version: "3.10" 23 | - name: Install PlatformIO Core 24 | run: pip install --upgrade platformio 25 | - name: Set environment variables 26 | run: echo "VERSION=$(head -1 version)" >> $GITHUB_ENV 27 | - name: Check if Git-Tag already exist 28 | run: | 29 | TAG="v${{ env.VERSION }}" 30 | if git show-ref --tags --verify --quiet "refs/tags/${TAG}" && [[ "${TAG}" != *"DEV"* ]]; then 31 | echo "Tag ${TAG} already exists" 32 | exit 1 33 | else 34 | echo "Tag ${TAG} does not exist or is DEV-Branch" 35 | fi 36 | - name: Check if version file contains linebreak at the end 37 | run: | 38 | if [ -n "$(tail -c 1 "version")" ]; then 39 | echo "The version file has no linebreak at the end" 40 | exit 1 41 | fi 42 | - name: Update hash in Global properties 43 | run: | 44 | sed -i -e 's|*GLOBAL_HASH = "\(.*\)";|*GLOBAL_HASH = "'"$(git rev-parse --short HEAD)"'";|g' components/cmd_system/cmd_system.c 45 | sed -i -e 's/DEV-VERSION/${{ env.VERSION }}/g' CMakeLists.txt 46 | - name: Install Python dependencies 47 | run: | 48 | python -m pip install --upgrade pip 49 | pip install htmlmin esptool 50 | - name: Minify HTML files 51 | run: python .github/workflows/shrinkHTML.py 52 | shell: sh 53 | - name: Build ESP32 firmware 54 | run: platformio run -e esp32 55 | - name: Build one binary for ESP32 56 | run: | 57 | folder=.pio/build/esp32 58 | chip=esp32 59 | mkdir -p release/ota/ESP32/ 60 | cp version release/ota/ 61 | cp $folder/firmware.bin release/ota/ESP32/firmware.bin 62 | mv $folder/firmware.bin $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 63 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x1000 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 64 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x1000 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 65 | mkdir -p release 66 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x1000 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 67 | mkdir -p release 68 | zip -j release/${chip}nat_extended_update_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 69 | zip -j release/${chip}nat_extended_full_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin 70 | 71 | - name: Build ESP32 C3 firmware 72 | run: platformio run -e esp32-c3 73 | - name: Build one binary for ESP32 C3 74 | run: | 75 | folder=.pio/build/esp32-c3 76 | chip=esp32c3 77 | mkdir -p release/ota/ESP32-C3/ 78 | cp $folder/firmware.bin release/ota/ESP32-C3/firmware.bin 79 | mv $folder/firmware.bin $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 80 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x0 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 81 | mkdir -p release/ 82 | zip -j release/${chip}nat_extended_update_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 83 | zip -j release/${chip}nat_extended_full_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin 84 | - name: Build ESP32 S2 firmware 85 | run: platformio run -e esp32-s2 86 | - name: Build one binary for ESP32 S2 87 | run: | 88 | folder=.pio/build/esp32-s2 89 | chip=esp32s2 90 | mkdir -p release/ota/ESP32-S2/ 91 | cp $folder/firmware.bin release/ota/ESP32-S2/firmware.bin 92 | mv $folder/firmware.bin $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 93 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x1000 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 94 | mkdir -p release 95 | zip -j release/${chip}nat_extended_update_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 96 | zip -j release/${chip}nat_extended_full_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin 97 | - name: Build ESP32 S3 firmware 98 | run: platformio run -e esp32-s3 99 | - name: Build one binary for ESP32 S3 100 | run: | 101 | folder=.pio/build/esp32-s3 102 | chip=esp32s3 103 | mkdir -p release/ota/ESP32-S3/ 104 | cp $folder/firmware.bin release/ota/ESP32-S3/firmware.bin 105 | mv $folder/firmware.bin $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 106 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x1000 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 107 | mkdir -p release 108 | zip -j release/${chip}nat_extended_update_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 109 | zip -j release/${chip}nat_extended_full_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin 110 | - name: Build ESP32 C6 firmware 111 | run: platformio run -e esp32-c6 112 | - name: Build one binary for ESP32 C6 113 | run: | 114 | folder=.pio/build/esp32-c6 115 | chip=esp32c6 116 | mkdir -p release/ota/ESP32-C6/ 117 | cp $folder/firmware.bin release/ota/ESP32-C6/firmware.bin 118 | mv $folder/firmware.bin $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 119 | esptool.py --chip $chip merge_bin -o $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin --flash_freq keep --flash_size keep 0x0 $folder/bootloader.bin 0x10000 $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 0x8000 $folder/partitions.bin 120 | mkdir -p release/ 121 | zip -j release/${chip}nat_extended_update_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_v${{ env.VERSION }}.bin 122 | zip -j release/${chip}nat_extended_full_v${{ env.VERSION }}.zip $folder/${chip}nat_extended_full_v${{ env.VERSION }}.bin 123 | - name: Create draft release 124 | uses: "marvinpinto/action-automatic-releases@latest" 125 | if: github.ref == 'refs/heads/master' 126 | with: 127 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 128 | prerelease: false 129 | draft: true 130 | automatic_release_tag: v${{ env.VERSION }} 131 | title: v${{ env.VERSION }} 132 | files: | 133 | release/*.zip 134 | - name: Create changelog from version file 135 | if: github.ref == 'refs/heads/master' 136 | run: tail -n +3 version | sed 's/^/- /' > changelog 137 | - name: Set changelog 138 | if: github.ref == 'refs/heads/master' 139 | uses: ncipollo/release-action@v1 140 | with: 141 | allowUpdates: true 142 | name: v${{ env.VERSION }} 143 | tag: v${{ env.VERSION }} 144 | draft: true 145 | bodyFile: "changelog" 146 | - name: Set dev branch variable 147 | if: github.ref == 'refs/heads/dev' 148 | run: | 149 | echo "RELEASE_BRANCH=releases-staging" >> $GITHUB_ENV 150 | - name: Set master branch variable 151 | if: github.ref == 'refs/heads/master' 152 | run: | 153 | echo "RELEASE_BRANCH=releases-production" >> $GITHUB_ENV 154 | - name: Deploy to Github Pages 155 | if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/master' 156 | uses: JamesIves/github-pages-deploy-action@v4 157 | with: 158 | folder: release/ota/ 159 | branch: ${{env.RELEASE_BRANCH}} 160 | - name: Upload firmwares to build 161 | uses: actions/upload-artifact@v4 162 | with: 163 | name: firmwares 164 | path: | 165 | release/*.zip 166 | 167 | -------------------------------------------------------------------------------- /.github/workflows/shrinkHTML.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, htmlmin, sys 4 | 5 | 6 | def shrinkHtml(): 7 | cwd = os.getcwd() 8 | print(cwd) 9 | directory = os.fsencode('src/pages/') 10 | 11 | for f in os.listdir(directory): 12 | filename = os.fsdecode(f) 13 | if filename.endswith(".html"): 14 | file = open(os.path.join(directory, f)) 15 | html = file.read().replace("\n", " ") 16 | file.close() 17 | minified = htmlmin.minify(html, remove_empty_space=True, remove_optional_attribute_quotes=False) 18 | print(minified) 19 | with open(os.path.join(directory, f), "w") as myfile: 20 | myfile.write(minified) 21 | continue 22 | else: 23 | continue 24 | 25 | 26 | def main(argv): 27 | shrinkHtml() 28 | 29 | 30 | if __name__ == "__main__": 31 | main(sys.argv[1:]) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/** 2 | .pio/** 3 | **/.vscode/** 4 | __pycache__/** 5 | release/** 6 | commands.txt 7 | ws.code-workspace -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | set(PROJECT_VER "DEV-VERSION") 3 | set(CONFIG_BOOTLOADER_APP_SECURE_VERSION "HASH") 4 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 5 | project(esp32_nat_router_extended) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 NAT Router Extended 2 | 3 | This is a firmware to use the ESP32 as WiFi NAT router. It can be used as 4 | - Simple range extender for an existing WiFi network 5 | - Setting up an additional WiFi network with different SSID/password for guests or IOT devices 6 | 7 | This is an extension of the great work of [martin-ger's ESP32 NAT Router-project](https://github.com/martin-ger/esp32_nat_router). I used his project as a starting point for learning microcontroller programming and extended it with some features for my use case. 8 | 9 | ## Features / Usage scenarios 10 | - Extend range of WiFi network (Repeater) 11 | - Additional network for guests 12 | - Portable usage with a small, low power device (battery powered) 13 | - [Bypass restrictions](docs/advanced.md#advanced-configuration) in public WiFis, like device and rate limit 14 | - Scanning for APs (s. [limitations](#wifi-scanning-limitation)) 15 | - User friendly UI with mobile support 16 | - [Resetting the device](docs/advanced.md#resetting-the-device-erasing-the-flash) in UI and with Pin/Button 17 | - [OTA-Updates](docs/ota.md) 18 | - Keep connection alive on networks with forced disconnect 19 | - Secure frontend by password or complete disabling 20 | - Show connected state and quality in UI 21 | - Disable the on board indicator LED 22 | 23 | ## First Boot 24 | After first boot the ESP32 NAT Router will offer a WiFi network with an open AP and the ssid "ESP32_NAT_Router". Configuration can either be done via a simple web interface or via the serial console. 25 | 26 | ## Web Config Interface 27 | The web interface allows for the configuration of all parameters. Connect you PC or smartphone to the WiFi SSID "ESP32_NAT_Router" and point your browser to "http://192.168.4.1". This page should appear: 28 | 29 | ![image](docs/index.png) 30 | 31 | First enter the appropriate values for the uplink WiFi network, the "STA Settings". Leave password blank for open networks. Click "Connect". The ESP32 reboots and will connect to your WiFi router. 32 | 33 | Now you can reconnect and reload the page and change the "Soft AP Settings". Click "Set" and again the ESP32 reboots. Now it is ready for forwarding traffic over the newly configured Soft AP. Be aware that these changes also affect the config interface, i.e. to do further configuration, connect to the ESP32 through one of the newly configured WiFi networks. 34 | 35 | ## Screenshots 36 | 37 | ![image](docs/scan.png) 38 | ![image](docs/enterprise_wifi.png) 39 | ![image](docs/advanced.png) 40 | ![image](docs/portmap.png) 41 | ![image](docs/reset.png) 42 | ![image](docs/lock.png) 43 | ![image](docs/ota.png) 44 | ![image](docs/unlock.png) 45 | ![image](docs/connected_clients.png) 46 | 47 | 48 | ## Flashing the prebuild binaries 49 | - Download [latest release](https://github.com/dchristl/esp32_nat_router_extended/releases/latest) 50 | * Download esp32nat_extended_full_vX.X.X.zip for fresh install 51 | * Download esp32nat_extended_update_vX.X.X.zip for update 52 | - Install [esptool](https://github.com/espressif/esptool) 53 | 54 | 55 | ### First install/ Reset 56 | 57 | If your device was used before for other projects or you want to reset all setting from previous version. Complete data loss! 58 | Unpack archive first and then execute: 59 | 60 | ``` 61 | esptool.py write_flash 0x0 esp32nat_extended_full_vX.X.X.bin 62 | ``` 63 | 64 | ### Update from older version 65 | If this project was already installed. No data loss from previous version. The preferred way is with [OTA-Updates](docs/ota.md). If you want to do it manually: 66 | 67 | ``` 68 | esptool.py write_flash 0x10000 esp32nat_extended_vX.X.X.bin 69 | ``` 70 | ### General 71 | 72 | If any problem occurs, erase flash manually before flashing the full version : 73 | ``` 74 | esptool.py erase_flash 75 | ``` 76 | 77 | 78 | ### Alternative way/ Graphical (Windows only) 79 | As an alternative you might use [Espressif's Flash Download Tools](https://www.espressif.com/en/support/download/other-tools). 80 | 81 | Check the marked parameters and files like below (ckeck the COM-Port for your environment). 82 | 83 | Check the addresses like below: 84 | 85 | ### First install/ Reset 86 | 87 | ![image](docs/win_flash_full.png) 88 | 89 | ### Update from older version 90 | 91 | ![image](docs/win_flash.png) 92 | 93 | ## Building the Binaries 94 | 95 | see [How to setup environment and build](docs/BUILD.md) 96 | 97 | 98 | ## Wifi scanning limitation 99 | Due to technical limitations, a client cannot be simultaneously connected to the device and scan for Wi-Fi networks. Before the scan starts, all the clients will be disconnected. After that, the scan will be saved in NVS,and the device will reboot. Upon reconnecting to the device, you will be able to view the scanned networks. 100 | 101 | An automatic redirect occurs the first time. Afterward, the scanned networks can be viewed three more times before they are deleted from the NVS to save storage space. 102 | 103 | ## Misc 104 | 105 | If you have any problems, suggestions for new features feel free to ask or raise an issue. This is a spare time project, I will answer if I'm free. 106 | If you like my work and want to support me, you can [buy me coffee](https://www.buymeacoffee.com/dchristl) or send me a donation via [PayPal](https://bit.ly/3Gde3KN) 107 | 108 | ## Advanced topics and configuration 109 | 110 | see [Advanced topics](docs/advanced.md) 111 | -------------------------------------------------------------------------------- /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.c: -------------------------------------------------------------------------------- 1 | /* Console example — NVS 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 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "esp_log.h" 16 | #include "esp_console.h" 17 | #include "argtable3/argtable3.h" 18 | #include "freertos/FreeRTOS.h" 19 | #include "freertos/event_groups.h" 20 | #include "esp_err.h" 21 | #include "cmd_nvs.h" 22 | #include "nvs.h" 23 | 24 | typedef struct { 25 | nvs_type_t type; 26 | const char *str; 27 | } type_str_pair_t; 28 | 29 | static const type_str_pair_t type_str_pair[] = { 30 | { NVS_TYPE_I8, "i8" }, 31 | { NVS_TYPE_U8, "u8" }, 32 | { NVS_TYPE_U16, "u16" }, 33 | { NVS_TYPE_I16, "i16" }, 34 | { NVS_TYPE_U32, "u32" }, 35 | { NVS_TYPE_I32, "i32" }, 36 | { NVS_TYPE_U64, "u64" }, 37 | { NVS_TYPE_I64, "i64" }, 38 | { NVS_TYPE_STR, "str" }, 39 | { NVS_TYPE_BLOB, "blob" }, 40 | { NVS_TYPE_ANY, "any" }, 41 | }; 42 | 43 | static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]); 44 | static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob"; 45 | static char current_namespace[16] = "storage"; 46 | static const char *TAG = "cmd_nvs"; 47 | 48 | static struct { 49 | struct arg_str *key; 50 | struct arg_str *type; 51 | struct arg_str *value; 52 | struct arg_end *end; 53 | } set_args; 54 | 55 | static struct { 56 | struct arg_str *key; 57 | struct arg_str *type; 58 | struct arg_end *end; 59 | } get_args; 60 | 61 | static struct { 62 | struct arg_str *key; 63 | struct arg_end *end; 64 | } erase_args; 65 | 66 | static struct { 67 | struct arg_str *namespace; 68 | struct arg_end *end; 69 | } erase_all_args; 70 | 71 | static struct { 72 | struct arg_str *namespace; 73 | struct arg_end *end; 74 | } namespace_args; 75 | 76 | static struct { 77 | struct arg_str *partition; 78 | struct arg_str *namespace; 79 | struct arg_str *type; 80 | struct arg_end *end; 81 | } list_args; 82 | 83 | 84 | static nvs_type_t str_to_type(const char *type) 85 | { 86 | for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { 87 | const type_str_pair_t *p = &type_str_pair[i]; 88 | if (strcmp(type, p->str) == 0) { 89 | return p->type; 90 | } 91 | } 92 | 93 | return NVS_TYPE_ANY; 94 | } 95 | 96 | static const char *type_to_str(nvs_type_t type) 97 | { 98 | for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { 99 | const type_str_pair_t *p = &type_str_pair[i]; 100 | if (p->type == type) { 101 | return p->str; 102 | } 103 | } 104 | 105 | return "Unknown"; 106 | } 107 | 108 | static esp_err_t store_blob(nvs_handle_t nvs, const char *key, const char *str_values) 109 | { 110 | uint8_t value; 111 | size_t str_len = strlen(str_values); 112 | size_t blob_len = str_len / 2; 113 | 114 | if (str_len % 2) { 115 | ESP_LOGE(TAG, "Blob data must contain even number of characters"); 116 | return ESP_ERR_NVS_TYPE_MISMATCH; 117 | } 118 | 119 | char *blob = (char *)malloc(blob_len); 120 | if (blob == NULL) { 121 | return ESP_ERR_NO_MEM; 122 | } 123 | 124 | for (int i = 0, j = 0; i < str_len; i++) { 125 | char ch = str_values[i]; 126 | if (ch >= '0' && ch <= '9') { 127 | value = ch - '0'; 128 | } else if (ch >= 'A' && ch <= 'F') { 129 | value = ch - 'A' + 10; 130 | } else if (ch >= 'a' && ch <= 'f') { 131 | value = ch - 'a' + 10; 132 | } else { 133 | ESP_LOGE(TAG, "Blob data contain invalid character"); 134 | free(blob); 135 | return ESP_ERR_NVS_TYPE_MISMATCH; 136 | } 137 | 138 | if (i & 1) { 139 | blob[j++] += value; 140 | } else { 141 | blob[j] = value << 4; 142 | } 143 | } 144 | 145 | esp_err_t err = nvs_set_blob(nvs, key, blob, blob_len); 146 | free(blob); 147 | 148 | if (err == ESP_OK) { 149 | err = nvs_commit(nvs); 150 | } 151 | 152 | return err; 153 | } 154 | 155 | static void print_blob(const char *blob, size_t len) 156 | { 157 | for (int i = 0; i < len; i++) { 158 | printf("%02x", blob[i]); 159 | } 160 | printf("\n"); 161 | } 162 | 163 | 164 | static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value) 165 | { 166 | esp_err_t err; 167 | nvs_handle_t nvs; 168 | bool range_error = false; 169 | 170 | nvs_type_t type = str_to_type(str_type); 171 | 172 | if (type == NVS_TYPE_ANY) { 173 | ESP_LOGE(TAG, "Type '%s' is undefined", str_type); 174 | return ESP_ERR_NVS_TYPE_MISMATCH; 175 | } 176 | 177 | err = nvs_open(current_namespace, NVS_READWRITE, &nvs); 178 | if (err != ESP_OK) { 179 | return err; 180 | } 181 | 182 | if (type == NVS_TYPE_I8) { 183 | int32_t value = strtol(str_value, NULL, 0); 184 | if (value < INT8_MIN || value > INT8_MAX || errno == ERANGE) { 185 | range_error = true; 186 | } else { 187 | err = nvs_set_i8(nvs, key, (int8_t)value); 188 | } 189 | } else if (type == NVS_TYPE_U8) { 190 | uint32_t value = strtoul(str_value, NULL, 0); 191 | if (value > UINT8_MAX || errno == ERANGE) { 192 | range_error = true; 193 | } else { 194 | err = nvs_set_u8(nvs, key, (uint8_t)value); 195 | } 196 | } else if (type == NVS_TYPE_I16) { 197 | int32_t value = strtol(str_value, NULL, 0); 198 | if (value < INT16_MIN || value > INT16_MAX || errno == ERANGE) { 199 | range_error = true; 200 | } else { 201 | err = nvs_set_i16(nvs, key, (int16_t)value); 202 | } 203 | } else if (type == NVS_TYPE_U16) { 204 | uint32_t value = strtoul(str_value, NULL, 0); 205 | if (value > UINT16_MAX || errno == ERANGE) { 206 | range_error = true; 207 | } else { 208 | err = nvs_set_u16(nvs, key, (uint16_t)value); 209 | } 210 | } else if (type == NVS_TYPE_I32) { 211 | int32_t value = strtol(str_value, NULL, 0); 212 | if (errno != ERANGE) { 213 | err = nvs_set_i32(nvs, key, value); 214 | } 215 | } else if (type == NVS_TYPE_U32) { 216 | uint32_t value = strtoul(str_value, NULL, 0); 217 | if (errno != ERANGE) { 218 | err = nvs_set_u32(nvs, key, value); 219 | } 220 | } else if (type == NVS_TYPE_I64) { 221 | int64_t value = strtoll(str_value, NULL, 0); 222 | if (errno != ERANGE) { 223 | err = nvs_set_i64(nvs, key, value); 224 | } 225 | } else if (type == NVS_TYPE_U64) { 226 | uint64_t value = strtoull(str_value, NULL, 0); 227 | if (errno != ERANGE) { 228 | err = nvs_set_u64(nvs, key, value); 229 | } 230 | } else if (type == NVS_TYPE_STR) { 231 | err = nvs_set_str(nvs, key, str_value); 232 | } else if (type == NVS_TYPE_BLOB) { 233 | err = store_blob(nvs, key, str_value); 234 | } 235 | 236 | if (range_error || errno == ERANGE) { 237 | nvs_close(nvs); 238 | return ESP_ERR_NVS_VALUE_TOO_LONG; 239 | } 240 | 241 | if (err == ESP_OK) { 242 | err = nvs_commit(nvs); 243 | if (err == ESP_OK) { 244 | ESP_LOGI(TAG, "Value stored under key '%s'", key); 245 | } 246 | } 247 | 248 | nvs_close(nvs); 249 | return err; 250 | } 251 | 252 | static esp_err_t get_value_from_nvs(const char *key, const char *str_type) 253 | { 254 | nvs_handle_t nvs; 255 | esp_err_t err; 256 | 257 | nvs_type_t type = str_to_type(str_type); 258 | 259 | if (type == NVS_TYPE_ANY) { 260 | ESP_LOGE(TAG, "Type '%s' is undefined", str_type); 261 | return ESP_ERR_NVS_TYPE_MISMATCH; 262 | } 263 | 264 | err = nvs_open(current_namespace, NVS_READONLY, &nvs); 265 | if (err != ESP_OK) { 266 | return err; 267 | } 268 | 269 | if (type == NVS_TYPE_I8) { 270 | int8_t value; 271 | err = nvs_get_i8(nvs, key, &value); 272 | if (err == ESP_OK) { 273 | printf("%d\n", value); 274 | } 275 | } else if (type == NVS_TYPE_U8) { 276 | uint8_t value; 277 | err = nvs_get_u8(nvs, key, &value); 278 | if (err == ESP_OK) { 279 | printf("%u\n", value); 280 | } 281 | } else if (type == NVS_TYPE_I16) { 282 | int16_t value; 283 | err = nvs_get_i16(nvs, key, &value); 284 | if (err == ESP_OK) { 285 | printf("%u\n", value); 286 | } 287 | } else if (type == NVS_TYPE_U16) { 288 | uint16_t value; 289 | if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) { 290 | printf("%u\n", value); 291 | } 292 | } else if (type == NVS_TYPE_I32) { 293 | int32_t value; 294 | if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) { 295 | printf("%"PRIi32"\n", value); 296 | } 297 | } else if (type == NVS_TYPE_U32) { 298 | uint32_t value; 299 | if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) { 300 | printf("%"PRIu32"\n", value); 301 | } 302 | } else if (type == NVS_TYPE_I64) { 303 | int64_t value; 304 | if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) { 305 | printf("%lld\n", value); 306 | } 307 | } else if (type == NVS_TYPE_U64) { 308 | uint64_t value; 309 | if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) { 310 | printf("%llu\n", value); 311 | } 312 | } else if (type == NVS_TYPE_STR) { 313 | size_t len; 314 | if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) { 315 | char *str = (char *)malloc(len); 316 | if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) { 317 | printf("%s\n", str); 318 | } 319 | free(str); 320 | } 321 | } else if (type == NVS_TYPE_BLOB) { 322 | size_t len; 323 | if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) { 324 | char *blob = (char *)malloc(len); 325 | if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) { 326 | print_blob(blob, len); 327 | } 328 | free(blob); 329 | } 330 | } 331 | 332 | nvs_close(nvs); 333 | return err; 334 | } 335 | 336 | static esp_err_t erase(const char *key) 337 | { 338 | nvs_handle_t nvs; 339 | 340 | esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs); 341 | if (err == ESP_OK) { 342 | err = nvs_erase_key(nvs, key); 343 | if (err == ESP_OK) { 344 | err = nvs_commit(nvs); 345 | if (err == ESP_OK) { 346 | ESP_LOGI(TAG, "Value with key '%s' erased", key); 347 | } 348 | } 349 | nvs_close(nvs); 350 | } 351 | 352 | return err; 353 | } 354 | 355 | static esp_err_t erase_all(const char *name) 356 | { 357 | nvs_handle_t nvs; 358 | 359 | esp_err_t err = nvs_open(name, NVS_READWRITE, &nvs); 360 | if (err == ESP_OK) { 361 | err = nvs_erase_all(nvs); 362 | if (err == ESP_OK) { 363 | err = nvs_commit(nvs); 364 | } 365 | } 366 | 367 | ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not"); 368 | 369 | nvs_close(nvs); 370 | return ESP_OK; 371 | } 372 | 373 | static int list(const char *part, const char *name, const char *str_type) 374 | { 375 | nvs_type_t type = str_to_type(str_type); 376 | 377 | nvs_iterator_t it = NULL; 378 | esp_err_t result = nvs_entry_find(part, NULL, type, &it); 379 | if (result == ESP_ERR_NVS_NOT_FOUND) { 380 | ESP_LOGE(TAG, "No such entry was found"); 381 | return 1; 382 | } 383 | 384 | if (result != ESP_OK) { 385 | ESP_LOGE(TAG, "NVS error: %s", esp_err_to_name(result)); 386 | return 1; 387 | } 388 | 389 | do { 390 | nvs_entry_info_t info; 391 | nvs_entry_info(it, &info); 392 | result = nvs_entry_next(&it); 393 | 394 | printf("namespace '%s', key '%s', type '%s' \n", 395 | info.namespace_name, info.key, type_to_str(info.type)); 396 | } while (result == ESP_OK); 397 | 398 | if (result != ESP_ERR_NVS_NOT_FOUND) { // the last iteration ran into an internal error 399 | ESP_LOGE(TAG, "NVS error %s at current iteration, stopping.", esp_err_to_name(result)); 400 | return 1; 401 | } 402 | 403 | return 0; 404 | } 405 | 406 | static int set_value(int argc, char **argv) 407 | { 408 | int nerrors = arg_parse(argc, argv, (void **) &set_args); 409 | if (nerrors != 0) { 410 | arg_print_errors(stderr, set_args.end, argv[0]); 411 | return 1; 412 | } 413 | 414 | const char *key = set_args.key->sval[0]; 415 | const char *type = set_args.type->sval[0]; 416 | const char *values = set_args.value->sval[0]; 417 | 418 | esp_err_t err = set_value_in_nvs(key, type, values); 419 | 420 | if (err != ESP_OK) { 421 | ESP_LOGE(TAG, "%s", esp_err_to_name(err)); 422 | return 1; 423 | } 424 | 425 | return 0; 426 | } 427 | 428 | static int get_value(int argc, char **argv) 429 | { 430 | int nerrors = arg_parse(argc, argv, (void **) &get_args); 431 | if (nerrors != 0) { 432 | arg_print_errors(stderr, get_args.end, argv[0]); 433 | return 1; 434 | } 435 | 436 | const char *key = get_args.key->sval[0]; 437 | const char *type = get_args.type->sval[0]; 438 | 439 | esp_err_t err = get_value_from_nvs(key, type); 440 | 441 | if (err != ESP_OK) { 442 | ESP_LOGE(TAG, "%s", esp_err_to_name(err)); 443 | return 1; 444 | } 445 | 446 | return 0; 447 | } 448 | 449 | static int erase_value(int argc, char **argv) 450 | { 451 | int nerrors = arg_parse(argc, argv, (void **) &erase_args); 452 | if (nerrors != 0) { 453 | arg_print_errors(stderr, erase_args.end, argv[0]); 454 | return 1; 455 | } 456 | 457 | const char *key = erase_args.key->sval[0]; 458 | 459 | esp_err_t err = erase(key); 460 | 461 | if (err != ESP_OK) { 462 | ESP_LOGE(TAG, "%s", esp_err_to_name(err)); 463 | return 1; 464 | } 465 | 466 | return 0; 467 | } 468 | 469 | static int erase_namespace(int argc, char **argv) 470 | { 471 | int nerrors = arg_parse(argc, argv, (void **) &erase_all_args); 472 | if (nerrors != 0) { 473 | arg_print_errors(stderr, erase_all_args.end, argv[0]); 474 | return 1; 475 | } 476 | 477 | const char *name = erase_all_args.namespace->sval[0]; 478 | 479 | esp_err_t err = erase_all(name); 480 | if (err != ESP_OK) { 481 | ESP_LOGE(TAG, "%s", esp_err_to_name(err)); 482 | return 1; 483 | } 484 | 485 | return 0; 486 | } 487 | 488 | static int set_namespace(int argc, char **argv) 489 | { 490 | int nerrors = arg_parse(argc, argv, (void **) &namespace_args); 491 | if (nerrors != 0) { 492 | arg_print_errors(stderr, namespace_args.end, argv[0]); 493 | return 1; 494 | } 495 | 496 | const char *namespace = namespace_args.namespace->sval[0]; 497 | strlcpy(current_namespace, namespace, sizeof(current_namespace)); 498 | ESP_LOGI(TAG, "Namespace set to '%s'", current_namespace); 499 | return 0; 500 | } 501 | int erase_ns(int argc, char **argv) 502 | { 503 | return erase_namespace(argc, argv); 504 | } 505 | 506 | 507 | static int list_entries(int argc, char **argv) 508 | { 509 | list_args.partition->sval[0] = ""; 510 | list_args.namespace->sval[0] = ""; 511 | list_args.type->sval[0] = ""; 512 | 513 | int nerrors = arg_parse(argc, argv, (void **) &list_args); 514 | if (nerrors != 0) { 515 | arg_print_errors(stderr, list_args.end, argv[0]); 516 | return 1; 517 | } 518 | 519 | const char *part = list_args.partition->sval[0]; 520 | const char *name = list_args.namespace->sval[0]; 521 | const char *type = list_args.type->sval[0]; 522 | 523 | return list(part, name, type); 524 | } 525 | 526 | void register_nvs(void) 527 | { 528 | set_args.key = arg_str1(NULL, NULL, "", "key of the value to be set"); 529 | set_args.type = arg_str1(NULL, NULL, "", ARG_TYPE_STR); 530 | 531 | set_args.value = arg_str1("v", "value", "", "value to be stored"); 532 | set_args.end = arg_end(2); 533 | 534 | get_args.key = arg_str1(NULL, NULL, "", "key of the value to be read"); 535 | get_args.type = arg_str1(NULL, NULL, "", ARG_TYPE_STR); 536 | get_args.end = arg_end(2); 537 | 538 | erase_args.key = arg_str1(NULL, NULL, "", "key of the value to be erased"); 539 | erase_args.end = arg_end(2); 540 | 541 | erase_all_args.namespace = arg_str1(NULL, NULL, "", "namespace to be erased"); 542 | erase_all_args.end = arg_end(2); 543 | 544 | namespace_args.namespace = arg_str1(NULL, NULL, "", "namespace of the partition to be selected"); 545 | namespace_args.end = arg_end(2); 546 | 547 | list_args.partition = arg_str1(NULL, NULL, "", "partition name"); 548 | list_args.namespace = arg_str0("n", "namespace", "", "namespace name"); 549 | list_args.type = arg_str0("t", "type", "", ARG_TYPE_STR); 550 | list_args.end = arg_end(2); 551 | 552 | const esp_console_cmd_t set_cmd = { 553 | .command = "nvs_set", 554 | .help = "Set key-value pair in selected namespace.\n" 555 | "Examples:\n" 556 | " nvs_set VarName i32 -v 123 \n" 557 | " nvs_set VarName str -v YourString \n" 558 | " nvs_set VarName blob -v 0123456789abcdef \n", 559 | .hint = NULL, 560 | .func = &set_value, 561 | .argtable = &set_args 562 | }; 563 | 564 | const esp_console_cmd_t get_cmd = { 565 | .command = "nvs_get", 566 | .help = "Get key-value pair from selected namespace. \n" 567 | "Example: nvs_get VarName i32", 568 | .hint = NULL, 569 | .func = &get_value, 570 | .argtable = &get_args 571 | }; 572 | 573 | const esp_console_cmd_t erase_cmd = { 574 | .command = "nvs_erase", 575 | .help = "Erase key-value pair from current namespace", 576 | .hint = NULL, 577 | .func = &erase_value, 578 | .argtable = &erase_args 579 | }; 580 | 581 | const esp_console_cmd_t erase_namespace_cmd = { 582 | .command = "nvs_erase_namespace", 583 | .help = "Erases specified namespace", 584 | .hint = NULL, 585 | .func = &erase_namespace, 586 | .argtable = &erase_all_args 587 | }; 588 | 589 | const esp_console_cmd_t namespace_cmd = { 590 | .command = "nvs_namespace", 591 | .help = "Set current namespace", 592 | .hint = NULL, 593 | .func = &set_namespace, 594 | .argtable = &namespace_args 595 | }; 596 | 597 | const esp_console_cmd_t list_entries_cmd = { 598 | .command = "nvs_list", 599 | .help = "List stored key-value pairs stored in NVS." 600 | "Namespace and type can be specified to print only those key-value pairs.\n" 601 | "Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t" 602 | "Example: nvs_list nvs -n storage -t u32 \n", 603 | .hint = NULL, 604 | .func = &list_entries, 605 | .argtable = &list_args 606 | }; 607 | 608 | ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd)); 609 | ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd)); 610 | ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd)); 611 | ESP_ERROR_CHECK(esp_console_cmd_register(&namespace_cmd)); 612 | ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd)); 613 | ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd)); 614 | } -------------------------------------------------------------------------------- /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 | { 14 | #endif 15 | 16 | // Register NVS functions 17 | void register_nvs(void); 18 | int erase_ns(int argc, char **argv); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /components/cmd_router/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "cmd_router.c" 2 | INCLUDE_DIRS . 3 | REQUIRES console nvs_flash driver spi_flash esp_wifi) 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/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 | 8 | #include "cc.h" 9 | #include "esp_wifi.h" 10 | 11 | #pragma once 12 | 13 | #ifdef __cplusplus 14 | extern "C" 15 | { 16 | #endif 17 | 18 | #define PARAM_NAMESPACE "esp32_nat" 19 | 20 | 21 | 22 | #define PROTO_TCP 6 23 | #define PROTO_UDP 17 24 | #define PORTMAP_MAX 32 25 | 26 | struct portmap_table_entry 27 | { 28 | u32_t daddr; 29 | u16_t mport; 30 | u16_t dport; 31 | u8_t proto; 32 | u8_t valid; 33 | }; 34 | extern struct portmap_table_entry portmap_tab[PORTMAP_MAX]; 35 | 36 | extern char *ssid; 37 | extern char *passwd; 38 | extern char *gateway_addr; 39 | extern char *ap_ssid; 40 | extern char *ap_passwd; 41 | 42 | 43 | extern bool ap_connect; 44 | 45 | extern uint32_t my_ip; 46 | extern uint32_t my_ap_ip; 47 | 48 | extern esp_netif_t *wifiSTA; 49 | int set_sta(int argc, char **argv); 50 | int set_sta_static(int argc, char **argv); 51 | int set_ap(int argc, char **argv); 52 | 53 | esp_err_t get_config_param_int(char *name, int32_t *param); 54 | esp_err_t get_config_param_str(char *name, char **param); 55 | esp_err_t get_config_param_blob(char *name, char **param, size_t *blob_len); 56 | esp_err_t get_config_param_blob2(char *name, uint8_t *blob, size_t blob_len); 57 | esp_err_t erase_key(char *name); 58 | 59 | void print_portmap_tab(); 60 | esp_err_t add_portmap(u8_t proto, u16_t mport, u32_t daddr, u16_t dport); 61 | esp_err_t del_portmap(u8_t proto, u16_t mport, u32_t daddr, u16_t dport); 62 | 63 | char *getDefaultIPByNetmask(); 64 | char *getNetmask(); 65 | 66 | #define DEFAULT_NETMASK_CLASS_A "255.0.0.0" 67 | #define DEFAULT_NETMASK_CLASS_B "255.255.0.0" 68 | #define DEFAULT_NETMASK_CLASS_C "255.255.255.0" 69 | 70 | #define DEFAULT_AP_IP_CLASS_A "10.0.%lu.1" 71 | #define DEFAULT_AP_IP_CLASS_B "172.16.%lu.1" 72 | #define DEFAULT_AP_IP_CLASS_C "192.168.%lu.1" 73 | 74 | /** 75 | * @brief Set ups and starts a simple DNS server that will respond to all queries 76 | * with the soft AP's IP address 77 | * 78 | */ 79 | void start_dns_server(); 80 | void stop_dns_server(); 81 | bool isDnsStarted(); 82 | uint16_t getConnectCount(); 83 | 84 | #define DEFAULT_SCAN_LIST_SIZE 15 85 | 86 | #ifdef __cplusplus 87 | } 88 | #endif 89 | -------------------------------------------------------------------------------- /components/cmd_system/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "cmd_system.c" 2 | INCLUDE_DIRS . 3 | REQUIRES console spi_flash driver esp_app_format) -------------------------------------------------------------------------------- /components/cmd_system/cmd_system.c: -------------------------------------------------------------------------------- 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 | 10 | #include 11 | #include 12 | #include 13 | #include "esp_log.h" 14 | #include "esp_console.h" 15 | #include "esp_system.h" 16 | #include "esp_sleep.h" 17 | #include "spi_flash_mmap.h" 18 | #include "driver/rtc_io.h" 19 | #include "driver/uart.h" 20 | #include "argtable3/argtable3.h" 21 | #include "freertos/FreeRTOS.h" 22 | #include "freertos/task.h" 23 | #include "cmd_system.h" 24 | #include "sdkconfig.h" 25 | #include "esp_chip_info.h" 26 | #include "esp_flash.h" 27 | #include "soc/soc_caps.h" 28 | #include "esp_app_desc.h" 29 | 30 | #ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS 31 | #define WITH_TASKS_INFO 1 32 | #endif 33 | 34 | static const char *TAG = "cmd_system"; 35 | const char *GLOBAL_HASH = "96ad523"; 36 | 37 | static void register_free(void); 38 | static void register_heap(void); 39 | static void register_version(void); 40 | static void register_restart(void); 41 | static void register_deep_sleep(void); 42 | static void register_light_sleep(void); 43 | #if WITH_TASKS_INFO 44 | static void register_tasks(void); 45 | #endif 46 | 47 | void register_system(void) 48 | { 49 | register_free(); 50 | register_heap(); 51 | register_version(); 52 | register_restart(); 53 | register_deep_sleep(); 54 | register_light_sleep(); 55 | #if WITH_TASKS_INFO 56 | register_tasks(); 57 | #endif 58 | } 59 | const char *get_project_version() 60 | { 61 | const esp_app_desc_t *app_desc = esp_app_get_description(); 62 | return app_desc->version; 63 | } 64 | 65 | const char *get_project_build_date() 66 | { 67 | const esp_app_desc_t *app_desc = esp_app_get_description(); 68 | return app_desc->date; 69 | } 70 | 71 | /* 'version' command */ 72 | static int get_version(int argc, char **argv) 73 | { 74 | 75 | char chip_type[30]; 76 | determineChipType(chip_type); 77 | esp_chip_info_t info; 78 | esp_chip_info(&info); 79 | uint32_t size_flash_chip; 80 | esp_flash_get_size(NULL, &size_flash_chip); 81 | const esp_app_desc_t *app_desc = esp_app_get_description(); 82 | 83 | printf("App name:\t%s\r\n", app_desc->project_name); 84 | printf("App version:\t%s-%s\r\n", app_desc->version, GLOBAL_HASH); 85 | printf("IDF version:\t%s\r\n", app_desc->idf_ver); 86 | printf("Build date:\t%s %s\r\n", app_desc->date, app_desc->time); 87 | printf("Chip info:\r\n"); 88 | printf("\t\tmodel: %s\r\n", chip_type); 89 | printf("\t\tcores: %d\r\n", info.cores); 90 | printf("\t\tfeature: %s%s%s%s%ld%s\r\n", 91 | info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "", 92 | info.features & CHIP_FEATURE_BLE ? "/BLE" : "", 93 | info.features & CHIP_FEATURE_BT ? "/BT" : "", 94 | info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash: " : "/External-Flash: ", 95 | size_flash_chip / (1024 * 1024), " MB"); 96 | printf("\t\trevision number: %d\r\n", info.revision); 97 | return 0; 98 | } 99 | 100 | static void register_version(void) 101 | { 102 | const esp_console_cmd_t cmd = { 103 | .command = "version", 104 | .help = "Get version of chip and SDK", 105 | .hint = NULL, 106 | .func = &get_version, 107 | }; 108 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 109 | } 110 | 111 | /** 'restart' command restarts the program */ 112 | 113 | static int restart(int argc, char **argv) 114 | { 115 | ESP_LOGI(TAG, "Restarting"); 116 | esp_restart(); 117 | } 118 | 119 | static void register_restart(void) 120 | { 121 | const esp_console_cmd_t cmd = { 122 | .command = "restart", 123 | .help = "Software reset of the chip", 124 | .hint = NULL, 125 | .func = &restart, 126 | }; 127 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 128 | } 129 | 130 | /** 'free' command prints available heap memory */ 131 | 132 | static int free_mem(int argc, char **argv) 133 | { 134 | printf("%ld\n", esp_get_free_heap_size()); 135 | return 0; 136 | } 137 | 138 | static void register_free(void) 139 | { 140 | const esp_console_cmd_t cmd = { 141 | .command = "free", 142 | .help = "Get the current size of free heap memory", 143 | .hint = NULL, 144 | .func = &free_mem, 145 | }; 146 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 147 | } 148 | 149 | /* 'heap' command prints minumum heap size */ 150 | static int heap_size(int argc, char **argv) 151 | { 152 | uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT); 153 | ESP_LOGI(TAG, "min heap size: %lu", heap_size); 154 | return 0; 155 | } 156 | 157 | static void register_heap(void) 158 | { 159 | const esp_console_cmd_t heap_cmd = { 160 | .command = "heap", 161 | .help = "Get minimum size of free heap memory that was available during program execution", 162 | .hint = NULL, 163 | .func = &heap_size, 164 | }; 165 | ESP_ERROR_CHECK(esp_console_cmd_register(&heap_cmd)); 166 | } 167 | 168 | /** 'tasks' command prints the list of tasks and related information */ 169 | #if WITH_TASKS_INFO 170 | 171 | static int tasks_info(int argc, char **argv) 172 | { 173 | const size_t bytes_per_task = 40; /* see vTaskList description */ 174 | char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); 175 | if (task_list_buffer == NULL) 176 | { 177 | ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output"); 178 | return 1; 179 | } 180 | fputs("Task Name\tStatus\tPrio\tHWM\tTask#", stdout); 181 | #ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID 182 | fputs("\tAffinity", stdout); 183 | #endif 184 | fputs("\n", stdout); 185 | vTaskList(task_list_buffer); 186 | fputs(task_list_buffer, stdout); 187 | free(task_list_buffer); 188 | return 0; 189 | } 190 | 191 | static void register_tasks(void) 192 | { 193 | const esp_console_cmd_t cmd = { 194 | .command = "tasks", 195 | .help = "Get information about running tasks", 196 | .hint = NULL, 197 | .func = &tasks_info, 198 | }; 199 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 200 | } 201 | 202 | #endif // WITH_TASKS_INFO 203 | 204 | /** 'deep_sleep' command puts the chip into deep sleep mode */ 205 | 206 | static struct 207 | { 208 | struct arg_int *wakeup_time; 209 | struct arg_int *wakeup_gpio_num; 210 | struct arg_int *wakeup_gpio_level; 211 | struct arg_end *end; 212 | } deep_sleep_args; 213 | 214 | static int deep_sleep(int argc, char **argv) 215 | { 216 | int nerrors = arg_parse(argc, argv, (void **)&deep_sleep_args); 217 | if (nerrors != 0) 218 | { 219 | arg_print_errors(stderr, deep_sleep_args.end, argv[0]); 220 | return 1; 221 | } 222 | if (deep_sleep_args.wakeup_time->count) 223 | { 224 | uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0]; 225 | ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout); 226 | ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(timeout)); 227 | } 228 | if (deep_sleep_args.wakeup_gpio_num->count) 229 | { 230 | int io_num = deep_sleep_args.wakeup_gpio_num->ival[0]; 231 | if (!GPIO_IS_VALID_GPIO(io_num)) 232 | { 233 | ESP_LOGE(TAG, "GPIO %d is not a valid IO", io_num); 234 | return 1; 235 | } 236 | int level = 1; 237 | if (deep_sleep_args.wakeup_gpio_level->count) 238 | { 239 | level = deep_sleep_args.wakeup_gpio_level->ival[0]; 240 | if (level != 0 && level != 1) 241 | { 242 | ESP_LOGE(TAG, "Invalid wakeup level: %d", level); 243 | return 1; 244 | } 245 | } 246 | ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level", 247 | io_num, level ? "HIGH" : "LOW"); 248 | 249 | #if defined(SOC_PM_SUPPORT_EXT1_WAKEUP) 250 | ESP_ERROR_CHECK(esp_sleep_enable_ext1_wakeup(1ULL << io_num, level)); 251 | #endif 252 | #if defined(SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP) 253 | ESP_ERROR_CHECK(esp_deep_sleep_enable_gpio_wakeup(1ULL << io_num, level)); 254 | #endif 255 | } 256 | 257 | #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) 258 | rtc_gpio_isolate(GPIO_NUM_12); 259 | #endif 260 | esp_deep_sleep_start(); 261 | } 262 | static void register_deep_sleep(void) 263 | { 264 | deep_sleep_args.wakeup_time = 265 | arg_int0("t", "time", "", "Wake up time, ms"); 266 | deep_sleep_args.wakeup_gpio_num = 267 | arg_int0(NULL, "io", "", 268 | "If specified, wakeup using GPIO with given number"); 269 | deep_sleep_args.wakeup_gpio_level = 270 | arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup"); 271 | deep_sleep_args.end = arg_end(3); 272 | 273 | const esp_console_cmd_t cmd = { 274 | .command = "deep_sleep", 275 | .help = "Enter deep sleep mode. " 276 | "Two wakeup modes are supported: timer and GPIO. " 277 | "If no wakeup option is specified, will sleep indefinitely.", 278 | .hint = NULL, 279 | .func = &deep_sleep, 280 | .argtable = &deep_sleep_args}; 281 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 282 | } 283 | 284 | /** 'light_sleep' command puts the chip into light sleep mode */ 285 | 286 | static struct 287 | { 288 | struct arg_int *wakeup_time; 289 | struct arg_int *wakeup_gpio_num; 290 | struct arg_int *wakeup_gpio_level; 291 | struct arg_end *end; 292 | } light_sleep_args; 293 | 294 | static int light_sleep(int argc, char **argv) 295 | { 296 | int nerrors = arg_parse(argc, argv, (void **)&light_sleep_args); 297 | if (nerrors != 0) 298 | { 299 | arg_print_errors(stderr, light_sleep_args.end, argv[0]); 300 | return 1; 301 | } 302 | esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); 303 | if (light_sleep_args.wakeup_time->count) 304 | { 305 | uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0]; 306 | ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout); 307 | ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(timeout)); 308 | } 309 | int io_count = light_sleep_args.wakeup_gpio_num->count; 310 | if (io_count != light_sleep_args.wakeup_gpio_level->count) 311 | { 312 | ESP_LOGE(TAG, "Should have same number of 'io' and 'io_level' arguments"); 313 | return 1; 314 | } 315 | for (int i = 0; i < io_count; ++i) 316 | { 317 | int io_num = light_sleep_args.wakeup_gpio_num->ival[i]; 318 | int level = light_sleep_args.wakeup_gpio_level->ival[i]; 319 | if (level != 0 && level != 1) 320 | { 321 | ESP_LOGE(TAG, "Invalid wakeup level: %d", level); 322 | return 1; 323 | } 324 | ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level", 325 | io_num, level ? "HIGH" : "LOW"); 326 | 327 | ESP_ERROR_CHECK(gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL)); 328 | } 329 | if (io_count > 0) 330 | { 331 | ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup()); 332 | } 333 | if (CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) 334 | { 335 | ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)"); 336 | ESP_ERROR_CHECK(uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3)); 337 | ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM)); 338 | } 339 | fflush(stdout); 340 | uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM); 341 | esp_light_sleep_start(); 342 | esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); 343 | const char *cause_str; 344 | switch (cause) 345 | { 346 | case ESP_SLEEP_WAKEUP_GPIO: 347 | cause_str = "GPIO"; 348 | break; 349 | case ESP_SLEEP_WAKEUP_UART: 350 | cause_str = "UART"; 351 | break; 352 | case ESP_SLEEP_WAKEUP_TIMER: 353 | cause_str = "timer"; 354 | break; 355 | default: 356 | cause_str = "unknown"; 357 | printf("%d\n", cause); 358 | } 359 | ESP_LOGI(TAG, "Woke up from: %s", cause_str); 360 | return 0; 361 | } 362 | 363 | static void register_light_sleep(void) 364 | { 365 | light_sleep_args.wakeup_time = 366 | arg_int0("t", "time", "", "Wake up time, ms"); 367 | light_sleep_args.wakeup_gpio_num = 368 | arg_intn(NULL, "io", "", 0, 8, 369 | "If specified, wakeup using GPIO with given number"); 370 | light_sleep_args.wakeup_gpio_level = 371 | arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup"); 372 | light_sleep_args.end = arg_end(3); 373 | 374 | const esp_console_cmd_t cmd = { 375 | .command = "light_sleep", 376 | .help = "Enter light sleep mode. " 377 | "Two wakeup modes are supported: timer and GPIO. " 378 | "Multiple GPIO pins can be specified using pairs of " 379 | "'io' and 'io_level' arguments. " 380 | "Will also wake up on UART input.", 381 | .hint = NULL, 382 | .func = &light_sleep, 383 | .argtable = &light_sleep_args}; 384 | ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); 385 | } 386 | void determineChipType(char chip_type[30]) 387 | { 388 | esp_chip_info_t chip_info; 389 | esp_chip_info(&chip_info); 390 | 391 | switch (chip_info.model) 392 | { 393 | case CHIP_ESP32: 394 | sprintf(chip_type, "%s", "ESP32"); 395 | break; 396 | case CHIP_ESP32S2: 397 | sprintf(chip_type, "%s", "ESP32-S2"); 398 | break; 399 | case CHIP_ESP32S3: 400 | sprintf(chip_type, "%s", "ESP32-S3"); 401 | break; 402 | case CHIP_ESP32C3: 403 | sprintf(chip_type, "%s", "ESP32-C3"); 404 | break; 405 | case CHIP_ESP32C6: 406 | sprintf(chip_type, "%s", "ESP32-C6"); 407 | break; 408 | default: 409 | int chip_model = chip_info.model; 410 | sprintf(chip_type, "%s (%d)", "Unknown/Unsupported", chip_model); 411 | break; 412 | } 413 | } -------------------------------------------------------------------------------- /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 | { 14 | #endif 15 | 16 | #ifndef CMD_SYSTEM_H 17 | #define CMD_SYSTEM_H 18 | 19 | extern const char *GLOBAL_HASH; 20 | #endif 21 | 22 | // Register system functions 23 | void register_system(void); 24 | void determineChipType(char chip_type[30]); 25 | const char *get_project_version(); 26 | const char *get_project_build_date(); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /docs/BUILD.md: -------------------------------------------------------------------------------- 1 | # How to set up environment and build binary 2 | 3 | ## Prerequisites 4 | 5 | - Install [VisualStudio Code](https://code.visualstudio.com/) 6 | - Install [PlatformIO IDE for VSCode](https://platformio.org/install/ide?install=vscode) 7 | - Install [Standard setup toolchain](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#get-started-get-prerequisites) for your platform 8 | 9 | 10 | ## Getting started 11 | 12 | - Checkout or download sources 13 | 14 | - Open project by selecting the project folder 15 | 16 | ![Start](pio_start.png) 17 | 18 | ![Open](pio_open.png) 19 | 20 | This may take a while, because PlatformIO downloads all the required boards and tools for development. 21 | 22 | - make your changes 23 | 24 | - you can now compile and/or upload your code 25 | 26 | ![Compile](pio_compile.png) 27 | 28 | - The bin-files (bootloader.bin, firmware.bin, partitions.bin) will be in {project folder}/.pio/build/esp32dev 29 | -------------------------------------------------------------------------------- /docs/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced topics 2 | 3 | ## Advanced configuration 4 | 5 | ![image](advanced.png) 6 | 7 | The settings under advanced configuration are well explained in the UI. 8 | 9 | ## Resetting the device/ Erasing the flash 10 | 11 | All settings can be wiped in the UI with the "Erase Flash"-Button. If any misconfiguration leads to an unusable device you can also connect GPIO 23 to ground on startup to reset the device copmpletely. The GND- and the GPIO 23-Pin are normally direct neighbours, so you can bridge them by any metallic object, like a paper clip, knife, small wire, ... If you want to reset, you have to power off the device, bridge the GND and GPIO 23 and power on the device. After 5s with bridged connection, the device is resetted and you can remove the bridge and restart the device. If on your board is an indicator LED available, these will flash fast during this process. 12 | 13 | ## Interpreting the on board LED 14 | 15 | If the ESP32 is connected to the upstream AP then the on board LED should be on, otherwise off. 16 | If there are devices connected to the ESP32 then the on board LED will keep blinking as many times as the number of devices connected. 17 | 18 | For example: 19 | 20 | One device connected to the ESP32, and the ESP32 is connected to upstream: 21 | 22 | `*****.*****` 23 | 24 | Two devices are connected to the ESP32, but the ESP32 is not connected to upstream: 25 | 26 | `....*.*....` 27 | 28 | ## Command Line Interface 29 | 30 | For configuration you have to use a serial console (Putty or GtkTerm with 115200 bps). 31 | Use the "set_sta" and the "set_ap" command to configure the WiFi settings. Changes are stored persistently in NVS and are applied after next restart. Use "show" to display the current config. The NVS namespace for the parameters is "esp32_nat" 32 | 33 | Enter the `help` command get a full list of all available commands: 34 | ``` 35 | help 36 | Print the list of registered commands 37 | 38 | free 39 | Get the current size of free heap memory 40 | 41 | heap 42 | Get minimum size of free heap memory that was available during program execu 43 | tion 44 | 45 | version 46 | Get version of chip and SDK 47 | 48 | restart 49 | Software reset of the chip 50 | 51 | deep_sleep [-t ] [--io=] [--io_level=<0|1>] 52 | Enter deep sleep mode. Two wakeup modes are supported: timer and GPIO. If no 53 | wakeup option is specified, will sleep indefinitely. 54 | -t, --time= Wake up time, ms 55 | --io= If specified, wakeup using GPIO with given number 56 | --io_level=<0|1> GPIO level to trigger wakeup 57 | 58 | light_sleep [-t ] [--io=]... [--io_level=<0|1>]... 59 | Enter light sleep mode. Two wakeup modes are supported: timer and GPIO. Mult 60 | iple GPIO pins can be specified using pairs of 'io' and 'io_level' arguments 61 | . Will also wake up on UART input. 62 | -t, --time= Wake up time, ms 63 | --io= If specified, wakeup using GPIO with given number 64 | --io_level=<0|1> GPIO level to trigger wakeup 65 | 66 | tasks 67 | Get information about running tasks 68 | 69 | nvs_set -v 70 | Set key-value pair in selected namespace. 71 | Examples: 72 | nvs_set VarName i32 -v 73 | 123 74 | nvs_set VarName str -v YourString 75 | nvs_set VarName blob -v 0123456789abcdef 76 | key of the value to be set 77 | type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob 78 | -v, --value= value to be stored 79 | 80 | nvs_get 81 | Get key-value pair from selected namespace. 82 | Example: nvs_get VarName i32 83 | key of the value to be read 84 | type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob 85 | 86 | nvs_erase 87 | Erase key-value pair from current namespace 88 | key of the value to be erased 89 | 90 | nvs_namespace 91 | Set current namespace 92 | namespace of the partition to be selected 93 | 94 | nvs_list [-n ] [-t ] 95 | List stored key-value pairs stored in NVS.Namespace and type can be specified 96 | to print only those key-value pairs. 97 | 98 | Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t 99 | Example: nvs_list nvs -n storage -t u32 100 | 101 | partition name 102 | -n, --namespace= namespace name 103 | -t, --type= type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob 104 | 105 | nvs_erase_namespace 106 | Erases specified namespace 107 | namespace to be erased 108 | 109 | set_sta 110 | Set SSID and password of the STA interface 111 | SSID 112 | Password 113 | 114 | set_sta_static 115 | Set Static IP for the STA interface 116 | IP 117 | Subnet Mask 118 | Gateway Address 119 | 120 | set_ap 121 | Set SSID and password of the SoftAP 122 | SSID of AP 123 | Password of AP 124 | 125 | set_sta_ent 126 | Set up WPA Enterprise in STA mode 127 | SSID 128 | Identity 129 | User 130 | Password 131 | 132 | set_ap_ip 133 | Set IP for the AP interface 134 | IP 135 | 136 | portmap [add|del] [TCP|UDP] 137 | Add or delete a portmapping to the router 138 | [add|del] add or delete portmapping 139 | [TCP|UDP] TCP or UDP port 140 | external port number 141 | internal IP 142 | internal port number 143 | 144 | show 145 | Get status and config of the router 146 | ``` 147 | ### NVS-Parameters in esp32 namespace 148 | 149 | | Parameter | Type | Hints 150 | | ----------- | ----------- | ------- | 151 | | ap_ssid | str |SSID of the AP network| 152 | | ap_passwd | str | Password of the AP network| 153 | | ssid_hidden | i32 | Hide the AP SSID | 154 | | ssid | str |SSID of the STA network| 155 | | keep_alive | i32 | Keep the connection alive| 156 | | led_disabled | i32 | Is the LED disabled| 157 | | nat_disabled | i32 | Is NAT disabled| 158 | | lock | i32 | Webserver is disabled| 159 | | custom_mac | str | Custom Mac address or "random"| 160 | | custom_dns | str | Custom DNS address| 161 | | hostname | str | Custom hostname| 162 | | octet | i32 | Custom third octet in the router's IP| 163 | | lock_pass | str | Password for the UI lock| 164 | | scan_result | str | Temporary parameter for the last scan result| 165 | | result_shown | i32 | Counter how many times the result was already shown (>3 = delete result)| 166 | | txpower | i32 | How much tx power should be used (between 8 and 84), larger, more power| 167 | | lower_bandwith | i32 | Use a lower bandwith (40 Mhz), but prefer more stable network| 168 | | netmask | str | Value of the network class to use (i.e. 255.255.255.0 or 255.255.255.128) | 169 | | sta_identity | str | Identity for WPA enterprise | 170 | | sta_user | str | WPA Enterprise username| 171 | | cer | str | Content of the WPA Enterprise certificate | 172 | | ota_url | str | Url to the binary for OTA-Updates | 173 | | canary | i32 | Use the canary/nightly builds for OTA-Updates | 174 | | loglevel | str | Sets the loglevel. Valid values: n -> log off; d -> log debug; v-> log verbose; i -> log info (default) | 175 | 176 | 177 | # DNS 178 | As soon as the ESP32 STA has learned a DNS IP from its upstream DNS server on first connect, it passes that to newly connected clients. 179 | Before that by default the DNS-Server which is offerd to clients connecting to the ESP32 AP is set to 192.168.4.1 and sets up a [Captive portal](https://en.wikipedia.org/wiki/Captive_portal). All DNS (http) resolutions will be resolved to 192.168.4.1 itself, so any input will lead to the start page. 180 | 181 | # Modified parameters compared to the default configuration 182 | 183 | | Location | Value | Hints 184 | | ----------- | ----------- | ------- | 185 | | Component config > LWIP > Enable copy between Layer2 and Layer3 packets. | Activated || 186 | | Component config > LWIP > Enable IP forwarding | Activated || 187 | | Component config > LWIP > Enable NAT (new/experimental) | Activated || 188 | | Component config > ESP-TLS > Allow potentially insecure options | Activated |For OTA-Updates| 189 | | Component config > ESP-TLS > Skip server certificate verification by default | Activated | For OTA-Updates | 190 | | Component config > ESP HTTPS OTA > Allow HTTP for OTA | Activated | For OTA-Updates with custom urls | 191 | | Component config > HTTP-Server > Max HTTP Request Header Length | 6144 | Max size for post requests (i.e. certificate) | 192 | | Component config > Log output > Maximum log verbosity | Verbose | To change the log level dynamically | 193 | -------------------------------------------------------------------------------- /docs/advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/advanced.png -------------------------------------------------------------------------------- /docs/connected_clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/connected_clients.png -------------------------------------------------------------------------------- /docs/enterprise_wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/enterprise_wifi.png -------------------------------------------------------------------------------- /docs/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/index.png -------------------------------------------------------------------------------- /docs/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/lock.png -------------------------------------------------------------------------------- /docs/ota.md: -------------------------------------------------------------------------------- 1 | # OTA Update 2 | 3 | ## Default 4 | 5 | The ESP32 OTA (Over-The-Air) process allows firmware updates to be performed on ESP32 devices wirelessly, without the need for physical access to the device. The new firmware will be downloaded to your device (the device must therefore be connected to an uplink network with internet access). After that it will be installed, followd by a reboot and the new firmware becomes active. 6 | 7 | 8 | ![image](ota.png) 9 | 10 | ![image](otalog.png) 11 | 12 | ## Updates with custom url 13 | 14 | It is also possible to specify a different URL for the update. This can be used, for example, to host your own version. 15 | You can achieve this by setting the 'ota_url' variable over the serial connection. This should be the complete path, including the '.bin' extension. 16 | 17 | ``` 18 | nvs_namespace esp32_nat 19 | nvs_set ota_url str -v http://192.168.0.20/firmware.bin 20 | ``` 21 | ![image](ota_custom.png) 22 | -------------------------------------------------------------------------------- /docs/ota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/ota.png -------------------------------------------------------------------------------- /docs/ota_custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/ota_custom.png -------------------------------------------------------------------------------- /docs/otalog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/otalog.png -------------------------------------------------------------------------------- /docs/pio_compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/pio_compile.png -------------------------------------------------------------------------------- /docs/pio_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/pio_open.png -------------------------------------------------------------------------------- /docs/pio_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/pio_start.png -------------------------------------------------------------------------------- /docs/portmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/portmap.png -------------------------------------------------------------------------------- /docs/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/reset.png -------------------------------------------------------------------------------- /docs/scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/scan.png -------------------------------------------------------------------------------- /docs/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/unlock.png -------------------------------------------------------------------------------- /docs/win_flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/win_flash.png -------------------------------------------------------------------------------- /docs/win_flash_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/docs/win_flash_full.png -------------------------------------------------------------------------------- /larger.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, ,0x2000, 4 | ota_0, app, ota_0, ,1500K 5 | ota_1, app, ota_1, ,1500K 6 | -------------------------------------------------------------------------------- /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 | [env] 12 | framework = espidf 13 | monitor_speed = 115200 14 | monitor_raw = yes 15 | board_build.partitions = larger.csv 16 | board_build.embed_files = 17 | src/pages/favicon.ico 18 | board_build.embed_txtfiles = 19 | src/pages/styles-67aa3b0203355627b525be2ea57be7bf.css 20 | src/pages/config.html 21 | src/pages/result.html 22 | src/pages/apply.html 23 | src/pages/scan.html 24 | src/pages/reset.html 25 | src/pages/unlock.html 26 | src/pages/advanced.html 27 | src/pages/lock.html 28 | src/pages/clients.html 29 | src/pages/ota.html 30 | src/pages/otalog.html 31 | src/pages/about.html 32 | src/pages/portmap_start.html 33 | src/pages/portmap_end.html 34 | src/pages/jquery-8a1045d9cbf50b52a0805c111ba08e94.js 35 | 36 | platform = espressif32 37 | ; platform = espressif32@6.5.0 38 | ; platform_packages = 39 | ; framework-espidf @ https://github.com/tasmota/esp-idf/releases/download/v5.1.2-org/esp-idf-v5.1.2-org.zip 40 | 41 | [env:esp32] 42 | board = esp32dev 43 | 44 | [env:esp32-c3] 45 | board = esp32-c3-devkitm-1 46 | 47 | [env:esp32-s2] 48 | board = lolin_s2_mini 49 | upload_port = /dev/ttyACM0 50 | 51 | [env:esp32-c6] 52 | board = esp32-c6-devkitm-1 53 | 54 | [env:esp32-s3] 55 | board = esp32-s3-devkitm-1 56 | -------------------------------------------------------------------------------- /refresh_styles: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")/src/pages" || exit 3 | purifycss -i -m -o minimized.css styles_normal.css *.html; mv minimized.css "styles-$(md5sum minimized.css | cut -d ' ' -f 1).css" 4 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 2 | 3 | idf_component_register(SRCS ${app_sources} INCLUDE_DIRS ".") 4 | 5 | target_add_binary_data(${COMPONENT_TARGET} "pages/favicon.ico" BINARY) 6 | target_add_binary_data(${COMPONENT_TARGET} "pages/styles-67aa3b0203355627b525be2ea57be7bf.css" TEXT) 7 | target_add_binary_data(${COMPONENT_TARGET} "pages/config.html" TEXT) 8 | target_add_binary_data(${COMPONENT_TARGET} "pages/result.html" TEXT) 9 | target_add_binary_data(${COMPONENT_TARGET} "pages/apply.html" TEXT) 10 | target_add_binary_data(${COMPONENT_TARGET} "pages/scan.html" TEXT) 11 | target_add_binary_data(${COMPONENT_TARGET} "pages/reset.html" TEXT) 12 | target_add_binary_data(${COMPONENT_TARGET} "pages/unlock.html" TEXT) 13 | target_add_binary_data(${COMPONENT_TARGET} "pages/advanced.html" TEXT) 14 | target_add_binary_data(${COMPONENT_TARGET} "pages/lock.html" TEXT) 15 | target_add_binary_data(${COMPONENT_TARGET} "pages/clients.html" TEXT) 16 | target_add_binary_data(${COMPONENT_TARGET} "pages/ota.html" TEXT) 17 | target_add_binary_data(${COMPONENT_TARGET} "pages/otalog.html" TEXT) 18 | target_add_binary_data(${COMPONENT_TARGET} "pages/about.html" TEXT) 19 | target_add_binary_data(${COMPONENT_TARGET} "pages/portmap_start.html" TEXT) 20 | target_add_binary_data(${COMPONENT_TARGET} "pages/portmap_end.html" TEXT) 21 | target_add_binary_data(${COMPONENT_TARGET} "pages/jquery-8a1045d9cbf50b52a0805c111ba08e94.js" TEXT) -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/dnserver.c: -------------------------------------------------------------------------------- 1 | /* Captive Portal Example 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 | 10 | #include 11 | 12 | #include "esp_log.h" 13 | #include "esp_system.h" 14 | #include "esp_netif.h" 15 | 16 | #include "lwip/err.h" 17 | #include "lwip/sockets.h" 18 | #include "lwip/sys.h" 19 | #include "lwip/netdb.h" 20 | #include "router_globals.h" 21 | #include "esp_wifi.h" 22 | 23 | #define DNS_PORT (53) 24 | #define DNS_MAX_LEN (256) 25 | 26 | #define OPCODE_MASK (0x7800) 27 | #define QR_FLAG (1 << 7) 28 | #define QD_TYPE_A (0x0001) 29 | #define ANS_TTL_SEC (300) 30 | 31 | static const char *TAG = "DNSServer"; 32 | TaskHandle_t task = NULL; 33 | 34 | // DNS Header Packet 35 | typedef struct __attribute__((__packed__)) 36 | { 37 | uint16_t id; 38 | uint16_t flags; 39 | uint16_t qd_count; 40 | uint16_t an_count; 41 | uint16_t ns_count; 42 | uint16_t ar_count; 43 | } dns_header_t; 44 | 45 | // DNS Question Packet 46 | typedef struct 47 | { 48 | uint16_t type; 49 | uint16_t class; 50 | } dns_question_t; 51 | 52 | // DNS Answer Packet 53 | typedef struct __attribute__((__packed__)) 54 | { 55 | uint16_t ptr_offset; 56 | uint16_t type; 57 | uint16_t class; 58 | uint32_t ttl; 59 | uint16_t addr_len; 60 | uint32_t ip_addr; 61 | } dns_answer_t; 62 | 63 | /* 64 | Parse the name from the packet from the DNS name format to a regular .-seperated name 65 | returns the pointer to the next part of the packet 66 | */ 67 | static char *parse_dns_name(char *raw_name, char *parsed_name, size_t parsed_name_max_len) 68 | { 69 | 70 | char *label = raw_name; 71 | char *name_itr = parsed_name; 72 | int name_len = 0; 73 | 74 | do 75 | { 76 | int sub_name_len = *label; 77 | // (len + 1) since we are adding a '.' 78 | name_len += (sub_name_len + 1); 79 | if (name_len > parsed_name_max_len) 80 | { 81 | return NULL; 82 | } 83 | 84 | // Copy the sub name that follows the the label 85 | memcpy(name_itr, label + 1, sub_name_len); 86 | name_itr[sub_name_len] = '.'; 87 | name_itr += (sub_name_len + 1); 88 | label += sub_name_len + 1; 89 | } while (*label != 0); 90 | 91 | // Terminate the final string, replacing the last '.' 92 | parsed_name[name_len - 1] = '\0'; 93 | // Return pointer to first char after the name 94 | return label + 1; 95 | } 96 | 97 | // Parses the DNS request and prepares a DNS response with the IP of the softAP 98 | static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t dns_reply_max_len) 99 | { 100 | if (req_len > dns_reply_max_len) 101 | { 102 | return -1; 103 | } 104 | 105 | // Prepare the reply 106 | memset(dns_reply, 0, dns_reply_max_len); 107 | memcpy(dns_reply, req, req_len); 108 | 109 | // Endianess of NW packet different from chip 110 | dns_header_t *header = (dns_header_t *)dns_reply; 111 | ESP_LOGD(TAG, "DNS query with header id: 0x%X, flags: 0x%X, qd_count: %d", 112 | ntohs(header->id), ntohs(header->flags), ntohs(header->qd_count)); 113 | 114 | // Not a standard query 115 | if ((header->flags & OPCODE_MASK) != 0) 116 | { 117 | return 0; 118 | } 119 | 120 | // Set question response flag 121 | header->flags |= QR_FLAG; 122 | 123 | uint16_t qd_count = ntohs(header->qd_count); 124 | header->an_count = htons(qd_count); 125 | 126 | int reply_len = qd_count * sizeof(dns_answer_t) + req_len; 127 | if (reply_len > dns_reply_max_len) 128 | { 129 | return -1; 130 | } 131 | 132 | // Pointer to current answer and question 133 | char *cur_ans_ptr = dns_reply + req_len; 134 | char *cur_qd_ptr = dns_reply + sizeof(dns_header_t); 135 | char name[128]; 136 | 137 | // Respond to all questions with the ESP32's IP address 138 | for (int i = 0; i < qd_count; i++) 139 | { 140 | char *name_end_ptr = parse_dns_name(cur_qd_ptr, name, sizeof(name)); 141 | if (name_end_ptr == NULL) 142 | { 143 | ESP_LOGE(TAG, "Failed to parse DNS question: %s", cur_qd_ptr); 144 | return -1; 145 | } 146 | 147 | dns_question_t *question = (dns_question_t *)(name_end_ptr); 148 | uint16_t qd_type = ntohs(question->type); 149 | uint16_t qd_class = ntohs(question->class); 150 | 151 | ESP_LOGD(TAG, "Received type: %d | Class: %d | Question for: %s", qd_type, qd_class, name); 152 | 153 | if (qd_type == QD_TYPE_A) 154 | { 155 | dns_answer_t *answer = (dns_answer_t *)cur_ans_ptr; 156 | 157 | answer->ptr_offset = htons(0xC000 | (cur_qd_ptr - dns_reply)); 158 | answer->type = htons(qd_type); 159 | answer->class = htons(qd_class); 160 | answer->ttl = htonl(ANS_TTL_SEC); 161 | 162 | esp_netif_ip_info_t ip_info; 163 | esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info); 164 | 165 | // ESP_LOGD(TAG, "Answer with PTR offset: 0x%X and IP 0x%X", ntohs(answer->ptr_offset), ip_info.ip.addr); 166 | answer->addr_len = htons(sizeof(ip_info.ip.addr)); 167 | answer->ip_addr = ip_info.ip.addr; 168 | } 169 | } 170 | return reply_len; 171 | } 172 | 173 | /* 174 | Sets up a socket and listen for DNS queries, 175 | replies to all type A queries with the IP of the softAP 176 | */ 177 | void dns_server_task(void *pvParameters) 178 | { 179 | char rx_buffer[128]; 180 | char addr_str[128]; 181 | int addr_family; 182 | int ip_protocol; 183 | 184 | while (1) 185 | { 186 | 187 | struct sockaddr_in dest_addr; 188 | dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); 189 | dest_addr.sin_family = AF_INET; 190 | dest_addr.sin_port = htons(DNS_PORT); 191 | addr_family = AF_INET; 192 | ip_protocol = IPPROTO_IP; 193 | inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); 194 | 195 | int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 196 | if (sock < 0) 197 | { 198 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 199 | break; 200 | } 201 | ESP_LOGI(TAG, "Socket created"); 202 | 203 | int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 204 | if (err < 0) 205 | { 206 | ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); 207 | } 208 | ESP_LOGI(TAG, "Socket bound, port %d", DNS_PORT); 209 | 210 | while (1) 211 | { 212 | ESP_LOGI(TAG, "Waiting for data"); 213 | struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 214 | socklen_t socklen = sizeof(source_addr); 215 | int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen); 216 | 217 | // Error occurred during receiving 218 | if (len < 0) 219 | { 220 | ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); 221 | close(sock); 222 | break; 223 | } 224 | // Data received 225 | else 226 | { 227 | // Get the sender's ip address as string 228 | if (source_addr.sin6_family == PF_INET) 229 | { 230 | inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); 231 | } 232 | else if (source_addr.sin6_family == PF_INET6) 233 | { 234 | inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); 235 | } 236 | 237 | // Null-terminate whatever we received and treat like a string... 238 | rx_buffer[len] = 0; 239 | 240 | char reply[DNS_MAX_LEN]; 241 | int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN); 242 | 243 | ESP_LOGI(TAG, "Received %d bytes from %s | DNS reply with len: %d", len, addr_str, reply_len); 244 | if (reply_len <= 0) 245 | { 246 | ESP_LOGE(TAG, "Failed to prepare a DNS reply"); 247 | } 248 | else 249 | { 250 | int err = sendto(sock, reply, reply_len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr)); 251 | if (err < 0) 252 | { 253 | ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); 254 | break; 255 | } 256 | } 257 | } 258 | } 259 | 260 | if (sock != -1) 261 | { 262 | ESP_LOGE(TAG, "Shutting down socket"); 263 | shutdown(sock, 0); 264 | close(sock); 265 | } 266 | } 267 | vTaskDelete(NULL); 268 | } 269 | 270 | bool isDnsStarted() 271 | { 272 | return task != NULL; 273 | } 274 | 275 | uint16_t getConnectCount() 276 | { 277 | 278 | wifi_sta_list_t wifi_sta_list; 279 | memset(&wifi_sta_list, 0, sizeof(wifi_sta_list)); 280 | 281 | esp_err_t err = esp_wifi_ap_get_sta_list(&wifi_sta_list); 282 | if (err == ESP_OK) 283 | { 284 | return wifi_sta_list.num; 285 | } 286 | return 0; 287 | } 288 | 289 | void start_dns_server() 290 | { 291 | xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, &task); 292 | ESP_LOGI(TAG, "DNS Server started"); 293 | } 294 | 295 | void stop_dns_server() 296 | { 297 | if (task != NULL) 298 | { 299 | vTaskDelete(task); 300 | task = NULL; 301 | ESP_LOGI(TAG, "DNS Server stopped"); 302 | } 303 | } -------------------------------------------------------------------------------- /src/http_server.c: -------------------------------------------------------------------------------- 1 | #include "urihandler/handler.h" 2 | 3 | #include "router_globals.h" 4 | #include "timer.h" 5 | 6 | static const char *TAG = "HTTPServer"; 7 | 8 | static httpd_uri_t applyp = { 9 | .uri = "/apply", 10 | .method = HTTP_POST, 11 | .handler = apply_post_handler, 12 | }; 13 | 14 | static httpd_uri_t applyg = { 15 | .uri = "/apply", 16 | .method = HTTP_GET, 17 | .handler = apply_get_handler, 18 | }; 19 | 20 | static httpd_uri_t indexg = { 21 | .uri = "/", 22 | .method = HTTP_GET, 23 | .handler = index_get_handler, 24 | }; 25 | static httpd_uri_t indexp = { 26 | .uri = "/", 27 | .method = HTTP_POST, 28 | .handler = index_post_handler, 29 | }; 30 | 31 | static httpd_uri_t resetg = { 32 | .uri = "/reset", 33 | .method = HTTP_GET, 34 | .handler = reset_get_handler, 35 | }; 36 | 37 | static httpd_uri_t unlockg = { 38 | .uri = "/unlock", 39 | .method = HTTP_GET, 40 | .handler = unlock_handler, 41 | }; 42 | static httpd_uri_t unlockp = { 43 | .uri = "/unlock", 44 | .method = HTTP_POST, 45 | .handler = unlock_handler, 46 | }; 47 | 48 | static httpd_uri_t lockp = { 49 | .uri = "/lock", 50 | .method = HTTP_POST, 51 | .handler = lock_handler, 52 | }; 53 | static httpd_uri_t lockg = { 54 | .uri = "/lock", 55 | .method = HTTP_GET, 56 | .handler = lock_handler, 57 | }; 58 | static httpd_uri_t apig = { 59 | .uri = "/api", 60 | .method = HTTP_GET, 61 | .handler = rest_handler, 62 | }; 63 | 64 | // URI handler for getting "html page" file 65 | static httpd_uri_t scan_page_download = { 66 | .uri = "/scan", 67 | .method = HTTP_GET, 68 | .handler = scan_download_get_handler, 69 | .user_ctx = NULL}; 70 | 71 | static httpd_uri_t result_page_download = { 72 | .uri = "/result", 73 | .method = HTTP_GET, 74 | .handler = result_download_get_handler, 75 | .user_ctx = NULL}; 76 | 77 | static httpd_uri_t clients_page_download = { 78 | .uri = "/clients", 79 | .method = HTTP_GET, 80 | .handler = clients_download_get_handler, 81 | .user_ctx = NULL}; 82 | 83 | static httpd_uri_t ota_page_download = { 84 | .uri = "/ota", 85 | .method = HTTP_GET, 86 | .handler = ota_download_get_handler, 87 | .user_ctx = NULL}; 88 | 89 | static httpd_uri_t otalog_page_download = { 90 | .uri = "/otalog", 91 | .method = HTTP_GET, 92 | .handler = otalog_get_handler, 93 | .user_ctx = NULL}; 94 | 95 | static httpd_uri_t ota_page_post = { 96 | .uri = "/ota", 97 | .method = HTTP_POST, 98 | .handler = ota_post_handler, 99 | }; 100 | 101 | static httpd_uri_t otalog_post_download = { 102 | .uri = "/otalog", 103 | .method = HTTP_POST, 104 | .handler = otalog_post_handler, 105 | .user_ctx = NULL}; 106 | 107 | static httpd_uri_t advanced_page_download = { 108 | .uri = "/advanced", 109 | .method = HTTP_GET, 110 | .handler = advanced_download_get_handler, 111 | .user_ctx = NULL}; 112 | 113 | static httpd_uri_t portmap_page_download = { 114 | .uri = "/portmap", 115 | .method = HTTP_GET, 116 | .handler = portmap_get_handler, 117 | .user_ctx = NULL}; 118 | 119 | static httpd_uri_t portmap_post_download = { 120 | .uri = "/portmap", 121 | .method = HTTP_POST, 122 | .handler = portmap_post_handler, 123 | .user_ctx = NULL}; 124 | 125 | // URI handler for getting favicon 126 | static httpd_uri_t favicon_handler = { 127 | .uri = "/favicon.ico", 128 | .method = HTTP_GET, 129 | .handler = favicon_get_handler, 130 | .user_ctx = NULL}; 131 | 132 | static httpd_uri_t jquery_handler = { 133 | .uri = "/jquery-8a1045d9cbf50b52a0805c111ba08e94.js", 134 | .method = HTTP_GET, 135 | .handler = jquery_get_handler, 136 | .user_ctx = NULL}; 137 | 138 | static httpd_uri_t about_handler = { 139 | .uri = "/about", 140 | .method = HTTP_GET, 141 | .handler = about_get_handler, 142 | .user_ctx = NULL}; 143 | 144 | static httpd_uri_t styles_handler = { 145 | .uri = "/styles-67aa3b0203355627b525be2ea57be7bf.css", 146 | .method = HTTP_GET, 147 | .handler = styles_download_get_handler, 148 | .user_ctx = NULL}; 149 | 150 | httpd_handle_t start_webserver(void) 151 | { 152 | httpd_handle_t server = NULL; 153 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 154 | config.max_uri_handlers = 25; 155 | config.stack_size = 16384; 156 | config.lru_purge_enable = true; 157 | 158 | initializeRestartTimer(); 159 | 160 | char *lock_pass = NULL; 161 | int32_t keepAlive = 0; 162 | 163 | get_config_param_str("lock_pass", &lock_pass); 164 | if (lock_pass != NULL && strlen(lock_pass) > 0) 165 | { 166 | lockUI(); 167 | ESP_LOGI(TAG, "UI is locked with password '%s'", lock_pass); 168 | } 169 | get_config_param_int("keep_alive", &keepAlive); 170 | if (keepAlive == 1) 171 | { 172 | initializeKeepAliveTimer(); 173 | ESP_LOGI(TAG, "Keep alive is enabled"); 174 | } 175 | else 176 | { 177 | ESP_LOGI(TAG, "Keep alive is disabled"); 178 | } 179 | 180 | // Start the httpd server 181 | ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); 182 | if (httpd_start(&server, &config) == ESP_OK) 183 | { 184 | // Set URI handlers 185 | ESP_LOGI(TAG, "Registering URI handlers"); 186 | httpd_register_uri_handler(server, &indexp); 187 | httpd_register_uri_handler(server, &indexg); 188 | httpd_register_uri_handler(server, &applyg); 189 | httpd_register_uri_handler(server, &applyp); 190 | httpd_register_uri_handler(server, &resetg); 191 | httpd_register_uri_handler(server, &scan_page_download); 192 | httpd_register_uri_handler(server, &result_page_download); 193 | httpd_register_uri_handler(server, &unlockg); 194 | httpd_register_uri_handler(server, &unlockp); 195 | httpd_register_uri_handler(server, &lockg); 196 | httpd_register_uri_handler(server, &lockp); 197 | httpd_register_uri_handler(server, &favicon_handler); 198 | httpd_register_uri_handler(server, &jquery_handler); 199 | httpd_register_uri_handler(server, &about_handler); 200 | httpd_register_uri_handler(server, &styles_handler); 201 | httpd_register_uri_handler(server, &apig); 202 | httpd_register_uri_handler(server, &advanced_page_download); 203 | httpd_register_uri_handler(server, &clients_page_download); 204 | httpd_register_uri_handler(server, &ota_page_download); 205 | httpd_register_uri_handler(server, &ota_page_post); 206 | httpd_register_uri_handler(server, &otalog_page_download); 207 | httpd_register_uri_handler(server, &otalog_post_download); 208 | httpd_register_uri_handler(server, &portmap_page_download); 209 | httpd_register_uri_handler(server, &portmap_post_download); 210 | httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler); 211 | return server; 212 | } 213 | 214 | ESP_LOGI(TAG, "Error starting server!"); 215 | return NULL; 216 | } -------------------------------------------------------------------------------- /src/pages/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | About 11 | 12 | 13 | 14 |
15 |
16 |

About

17 |
18 |
19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 49 | 50 | 51 |
ESP32 NAT Router is a simple micro controller based range extender. This 23 | can also be used to open a second (guest) Wifi.

This is a spare time project. If 24 | you have any questions or issues, feel free to ask at the Github project page. 25 |

26 |
Version%s
Hash%s
Release date%s
Project 43 | page at GitHub
Support my work, 48 | just buy me coffee
52 |
53 | 54 |
55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/pages/advanced.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Advanced 11 | 12 | 13 | 14 |
15 |
16 |

Advanced configuration

17 |
18 |
19 |

Misc

20 | 21 |
22 |
23 | 24 | 27 |
28 |
29 | 30 | 32 |
33 | 40 | 41 |
42 |
43 |
44 | 45 | 51 |
52 |
53 | 54 | 59 |
60 | 61 | 71 | 72 |
73 |
74 |
77 |
78 | 83 |
84 |
85 |
89 |
90 | 95 |
96 |
97 |
100 |
101 | 106 |
107 |
108 |
112 |
113 | 116 |
117 | 118 |

DNS override

Your current DNS is: %s 119 |
120 |
123 |
127 |
128 |
132 |
137 | 144 |
145 |

MAC override

Your current MAC address is: %s 147 |
148 |
152 |
156 |
162 | 172 |
173 |

Netmask override

Your current netmask is: %s 175 |
176 |
178 | 181 |
182 |
186 |
190 |
195 |
196 | 202 |
203 |
205 |
206 |
207 |
209 |
210 | 211 |
212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /src/pages/apply.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Apply changes 14 | 15 | 16 | 17 | 18 |
19 |
20 |

ESP32 NAT Router Config

21 |
22 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /src/pages/clients.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Connected clients 11 | 12 | 13 | 14 |
15 |
16 |

Connected clients

17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | %s 27 |
#IP addressMAC
28 | 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/pages/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Configuration 11 | 12 | 13 | 14 |
15 |
16 |

ESP32 NAT Router Config ?

17 |
18 | 19 |
20 |

AP Settings (the new network)

%d client(s) connected 22 | 23 | 24 |
25 | 26 | 27 |
28 |
29 |
31 |
32 |
33 |
34 |
36 | 39 | 41 | 43 | 44 |
45 |
46 |
47 |
Password less than 8 chars = 48 | open
49 | 50 |

STA Settings (uplink WiFi network)

51 |

53 | 55 | 56 | 58 | 60 | 62 | (signal strength: %s db) 63 |

64 |
65 | 66 | 67 |
68 |
69 |
71 |
72 | 73 |
74 |
76 |
77 |
78 |
80 |
81 |
82 |
83 | 85 |
86 |
87 | 88 |
89 |
90 |
91 |
96 | 98 | 100 | 101 | 102 |
103 |
104 |
105 |
107 |
108 |

Device Management

109 | 110 | 122 |
123 | OTA Updates 124 |
125 |
126 | Portmap configuration 127 |
128 | Lock 129 | interface 130 | 131 | 141 | Advanced 142 | 143 |
144 | 145 | 146 | 166 | 167 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/pages/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/214cc0bfb8fdc5b10b5843fbdbb45c9278e5b92d/src/pages/favicon.ico -------------------------------------------------------------------------------- /src/pages/lock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Lock UI 11 | 12 | 13 | 14 |
15 |
16 |

Lock UI

17 |
18 |

To lock interface insert a password twice.

19 |
20 |
21 |
23 |
24 |
25 |
27 |
28 |
Both fields empty will 29 | remove the password
30 |
32 |
33 |
34 |
37 |
38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /src/pages/ota.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | OTA Update 11 | 12 | 13 | 14 |
15 |
16 |

OTA Update

17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
Current version%s
Latest version%s
Changelog 31 |
    %s
32 |
Update Source%s
Chip type%s
44 | 45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /src/pages/otalog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | OTA Update 12 | 13 | 14 | 15 |
16 |
17 |

OTA Update

18 |
19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | %s 34 | 35 | %s 36 | 37 |
24 |
25 |
%s
27 |
28 |
OTA update started with %s
38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /src/pages/portmap_end.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Add new portmap

4 |
5 | 6 |
7 |
8 | 13 |
14 |
15 | 17 |
18 |
19 |
20 | %s 21 | 23 |
24 |
25 |
26 | 28 |
29 |
30 |
31 | 38 |
39 |
40 |
41 | 56 |
57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/pages/portmap_start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Portmap configuration 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |

Portmap configuration

28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/pages/reset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Reset 13 | 14 | 15 | 16 | 17 |
18 |
19 |

Reset Device

20 |
21 | 27 |
28 |
30 | 31 | 32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /src/pages/result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Scan 11 | 12 | 13 | 14 |
15 |
16 |

SSID Scan

17 |
18 |
ProtocolExternal portInternal IPInternal portAction
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | %s 27 |
SSIDQualityAction
28 | 29 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /src/pages/scan.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Scan 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Scan for networks

21 |
22 | 27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/pages/unlock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Unlock UI 11 | 12 | 13 | 14 |
15 |
16 |

Unlock

17 |
18 |

Interface is locked. Insert your unlock password to continue.

19 |
20 |
21 |
23 |
24 |
26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/scan.c: -------------------------------------------------------------------------------- 1 | /* Scan Example 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 | 10 | /* 11 | This example shows how to scan for available set of APs. 12 | */ 13 | 14 | #include "scan.h" 15 | #include "router_globals.h" 16 | 17 | static const char *TAG = "Wifi-Scan"; 18 | 19 | static void print_auth_mode(int authmode) 20 | { 21 | switch (authmode) 22 | { 23 | case WIFI_AUTH_OPEN: 24 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OPEN"); 25 | break; 26 | case WIFI_AUTH_OWE: 27 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OWE"); 28 | break; 29 | case WIFI_AUTH_WEP: 30 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WEP"); 31 | break; 32 | case WIFI_AUTH_WPA_PSK: 33 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_PSK"); 34 | break; 35 | case WIFI_AUTH_WPA2_PSK: 36 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_PSK"); 37 | break; 38 | case WIFI_AUTH_WPA_WPA2_PSK: 39 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_WPA2_PSK"); 40 | break; 41 | case WIFI_AUTH_ENTERPRISE: 42 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_ENTERPRISE"); 43 | break; 44 | case WIFI_AUTH_WPA3_PSK: 45 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_PSK"); 46 | break; 47 | case WIFI_AUTH_WPA2_WPA3_PSK: 48 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_PSK"); 49 | break; 50 | case WIFI_AUTH_WPA3_ENT_192: 51 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_ENT_192"); 52 | break; 53 | default: 54 | ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_UNKNOWN"); 55 | break; 56 | } 57 | } 58 | static void print_cipher_type(int pairwise_cipher, int group_cipher) 59 | { 60 | switch (pairwise_cipher) 61 | { 62 | case WIFI_CIPHER_TYPE_NONE: 63 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_NONE"); 64 | break; 65 | case WIFI_CIPHER_TYPE_WEP40: 66 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP40"); 67 | break; 68 | case WIFI_CIPHER_TYPE_WEP104: 69 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP104"); 70 | break; 71 | case WIFI_CIPHER_TYPE_TKIP: 72 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP"); 73 | break; 74 | case WIFI_CIPHER_TYPE_CCMP: 75 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_CCMP"); 76 | break; 77 | case WIFI_CIPHER_TYPE_TKIP_CCMP: 78 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP"); 79 | break; 80 | case WIFI_CIPHER_TYPE_AES_CMAC128: 81 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_AES_CMAC128"); 82 | break; 83 | case WIFI_CIPHER_TYPE_SMS4: 84 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_SMS4"); 85 | break; 86 | case WIFI_CIPHER_TYPE_GCMP: 87 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP"); 88 | break; 89 | case WIFI_CIPHER_TYPE_GCMP256: 90 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP256"); 91 | break; 92 | default: 93 | ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_UNKNOWN"); 94 | break; 95 | } 96 | 97 | switch (group_cipher) 98 | { 99 | case WIFI_CIPHER_TYPE_NONE: 100 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_NONE"); 101 | break; 102 | case WIFI_CIPHER_TYPE_WEP40: 103 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP40"); 104 | break; 105 | case WIFI_CIPHER_TYPE_WEP104: 106 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP104"); 107 | break; 108 | case WIFI_CIPHER_TYPE_TKIP: 109 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP"); 110 | break; 111 | case WIFI_CIPHER_TYPE_CCMP: 112 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_CCMP"); 113 | break; 114 | case WIFI_CIPHER_TYPE_TKIP_CCMP: 115 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP"); 116 | break; 117 | case WIFI_CIPHER_TYPE_SMS4: 118 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_SMS4"); 119 | break; 120 | case WIFI_CIPHER_TYPE_GCMP: 121 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP"); 122 | break; 123 | case WIFI_CIPHER_TYPE_GCMP256: 124 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP256"); 125 | break; 126 | default: 127 | ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_UNKNOWN"); 128 | break; 129 | } 130 | } 131 | 132 | /* Initialize Wi-Fi as sta and set scan method */ 133 | static char *wifi_scan(void) 134 | { 135 | vTaskDelay(1000 / portTICK_PERIOD_MS); 136 | esp_wifi_disconnect(); 137 | vTaskDelay(1000 / portTICK_PERIOD_MS); 138 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 139 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 140 | 141 | uint16_t number = DEFAULT_SCAN_LIST_SIZE; 142 | wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE]; 143 | uint16_t ap_count = 0; 144 | memset(ap_info, 0, sizeof(ap_info)); 145 | 146 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 147 | ESP_ERROR_CHECK(esp_wifi_start()); 148 | 149 | esp_wifi_scan_start(NULL, true); 150 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count)); 151 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info)); 152 | ESP_LOGI(TAG, "Total APs scanned = %u", ap_count); 153 | 154 | char result[DEFAULT_SCAN_LIST_SIZE * 100]; 155 | strcpy(result, ""); 156 | 157 | for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) 158 | { 159 | ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid); 160 | ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi); 161 | print_auth_mode(ap_info[i].authmode); 162 | if (ap_info[i].authmode != WIFI_AUTH_WEP) 163 | { 164 | print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher); 165 | } 166 | ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary); 167 | 168 | char *tmp = malloc(100); 169 | 170 | sprintf(tmp, "%s\x03%d\x05", ap_info[i].ssid, ap_info[i].rssi); 171 | 172 | strcat(result, tmp); 173 | free(tmp); 174 | } 175 | 176 | char *a_ptr = result; 177 | 178 | return a_ptr; 179 | 180 | } 181 | 182 | void fillNodes() 183 | { 184 | // Initialize NVS 185 | esp_err_t ret = nvs_flash_init(); 186 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 187 | { 188 | ESP_ERROR_CHECK(nvs_flash_erase()); 189 | ret = nvs_flash_init(); 190 | } 191 | ESP_ERROR_CHECK(ret); 192 | 193 | const char *scan_result = wifi_scan(); 194 | nvs_handle_t nvs; 195 | ESP_ERROR_CHECK(nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs)); 196 | ESP_ERROR_CHECK(nvs_set_str(nvs, "scan_result", scan_result)); 197 | nvs_erase_key(nvs, "result_shown"); 198 | 199 | ESP_ERROR_CHECK(nvs_commit(nvs)); 200 | nvs_close(nvs); 201 | esp_restart(); 202 | } -------------------------------------------------------------------------------- /src/scan.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "freertos/FreeRTOS.h" 3 | #include "esp_wifi.h" 4 | #include "esp_log.h" 5 | #include "nvs_flash.h" 6 | #include "router_globals.h" 7 | 8 | void fillNodes(); 9 | char *findTextColorForSSID(int8_t rssi); -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include 3 | #include 4 | #include "esp_timer.h" 5 | #include "esp_http_client.h" 6 | 7 | static const char *TAG = "Timer"; 8 | 9 | #define REFRESH_TIMER_PERIOD 5 * 60000000 10 | 11 | esp_timer_handle_t restart_timer, refresh_timer; 12 | 13 | static void restart_timer_callback(void *arg) 14 | { 15 | ESP_LOGI(TAG, "Restarting now..."); 16 | esp_restart(); 17 | } 18 | static void refresh_timer_callback(void *arg) 19 | { 20 | ESP_LOGI(TAG, "Starting web call for keep alive"); 21 | esp_http_client_config_t config = { 22 | .url = "https://www.startpage.com/", 23 | .method = HTTP_METHOD_HEAD, 24 | .disable_auto_redirect = true}; 25 | esp_http_client_handle_t client = esp_http_client_init(&config); 26 | 27 | // GET 28 | esp_err_t err = esp_http_client_perform(client); 29 | if (err == ESP_OK) 30 | { 31 | ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %lld ", esp_http_client_get_status_code(client), 32 | esp_http_client_get_content_length(client)); 33 | } 34 | else 35 | { 36 | ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err)); 37 | } 38 | esp_http_client_cleanup(client); 39 | } 40 | 41 | esp_timer_create_args_t restart_timer_args = { 42 | .callback = &restart_timer_callback, 43 | /* argument specified here will be passed to timer callback function */ 44 | .arg = (void *)0, 45 | .name = "restart_timer"}; 46 | 47 | esp_timer_create_args_t refresh_timer_args = { 48 | .callback = &refresh_timer_callback, 49 | .arg = (void *)0, 50 | .name = "refresh_timer"}; 51 | 52 | void initializeRestartTimer() 53 | { 54 | esp_timer_create(&restart_timer_args, &restart_timer); 55 | } 56 | void initializeKeepAliveTimer() 57 | { 58 | esp_timer_create(&refresh_timer_args, &refresh_timer); 59 | esp_timer_start_periodic(refresh_timer, REFRESH_TIMER_PERIOD); 60 | } 61 | 62 | void restartByTimer() 63 | { 64 | esp_timer_start_once(restart_timer, 500000); 65 | } 66 | 67 | void restartByTimerinS(int seconds) 68 | { 69 | esp_timer_start_once(restart_timer, seconds * 1000000); 70 | } 71 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | void initializeRestartTimer(); 2 | void initializeKeepAliveTimer(); 3 | void restartByTimer(); 4 | void restartByTimerinS(int seconds); -------------------------------------------------------------------------------- /src/urihandler/abouthandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | 3 | static const char *TAG = "AboutHandler"; 4 | 5 | esp_err_t about_get_handler(httpd_req_t *req) 6 | { 7 | 8 | ESP_LOGI(TAG, "Requesting about page"); 9 | if (isLocked()) 10 | { 11 | return redirectToLock(req); 12 | } 13 | httpd_req_to_sockfd(req); 14 | 15 | extern const char about_start[] asm("_binary_about_html_start"); 16 | extern const char about_end[] asm("_binary_about_html_end"); 17 | const size_t about_html_size = (about_end - about_start); 18 | 19 | const char *project_version = get_project_version(); 20 | const char *project_build_date = get_project_build_date(); 21 | char *about_page = malloc(about_html_size + strlen(project_version) + strlen(GLOBAL_HASH) + strlen(project_build_date) + 1); 22 | 23 | sprintf(about_page, about_start, project_version, GLOBAL_HASH, project_build_date); 24 | 25 | closeHeader(req); 26 | 27 | esp_err_t out = httpd_resp_send(req, about_page, HTTPD_RESP_USE_STRLEN); 28 | free(about_page); 29 | return out; 30 | } -------------------------------------------------------------------------------- /src/urihandler/advancedhandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include 3 | #include "dhcpserver/dhcpserver.h" 4 | #include "router_globals.h" 5 | #include "esp_wifi.h" 6 | #include "esp_mac.h" 7 | #include "esp_err.h" 8 | 9 | static const char *TAG = "Advancedhandler"; 10 | 11 | esp_err_t advanced_download_get_handler(httpd_req_t *req) 12 | { 13 | if (isLocked()) 14 | { 15 | return redirectToLock(req); 16 | } 17 | 18 | httpd_req_to_sockfd(req); 19 | extern const char advanced_start[] asm("_binary_advanced_html_start"); 20 | extern const char advanced_end[] asm("_binary_advanced_html_end"); 21 | const size_t advanced_html_size = (advanced_end - advanced_start); 22 | 23 | int32_t keepAlive = 0; 24 | int32_t ledDisabled = 0; 25 | int32_t natDisabled = 0; 26 | char *aliveCB = ""; 27 | char *ledCB = ""; 28 | char *natCB = ""; 29 | char *currentDNS = ""; 30 | char *defCB = ""; 31 | char *cloudCB = ""; 32 | char *adguardCB = ""; 33 | char *customCB = ""; 34 | char *customDNSIP = ""; 35 | char *defMacCB = ""; 36 | char *rndMacCB = ""; 37 | char *customMacCB = ""; 38 | char *customMac = ""; 39 | char *macSetting = ""; 40 | char *classACB = ""; 41 | char *classBCB = ""; 42 | char *classCCB = ""; 43 | char *customMaskCB = ""; 44 | char *customMask = ""; 45 | 46 | char currentMAC[18]; 47 | char defaultMAC[18]; 48 | 49 | char *hostName = NULL; 50 | int32_t octet = 4; 51 | get_config_param_str("hostname", &hostName); 52 | get_config_param_int("octet", &octet); 53 | 54 | int32_t txPower = 0; 55 | get_config_param_int("txpower", &txPower); 56 | if (txPower < 8 || txPower > 84) 57 | { 58 | txPower = 80; // default 59 | } 60 | char *lowSelected, *mediumSelected, *highSelected = NULL; 61 | if (txPower < 34) 62 | { // low 63 | lowSelected = "selected"; 64 | mediumSelected = ""; 65 | highSelected = ""; 66 | } 67 | else if (txPower < 60) 68 | { // medium 69 | lowSelected = ""; 70 | mediumSelected = "selected"; 71 | highSelected = ""; 72 | } 73 | else 74 | { // high 75 | lowSelected = ""; 76 | mediumSelected = ""; 77 | highSelected = "selected"; 78 | } 79 | 80 | int32_t bandwith = 0; 81 | get_config_param_int("lower_bandwith", &bandwith); 82 | char *bwLow, *bwHigh = NULL; 83 | if (bandwith == 1) 84 | { 85 | bwLow = "selected"; 86 | bwHigh = ""; 87 | } 88 | else 89 | { 90 | bwLow = ""; 91 | bwHigh = "selected"; 92 | } 93 | 94 | get_config_param_int("keep_alive", &keepAlive); 95 | if (keepAlive == 1) 96 | { 97 | aliveCB = "checked"; 98 | } 99 | get_config_param_int("led_disabled", &ledDisabled); 100 | if (ledDisabled == 0) 101 | { 102 | ledCB = "checked"; 103 | } 104 | get_config_param_int("nat_disabled", &natDisabled); 105 | if (natDisabled == 0) 106 | { 107 | natCB = "checked"; 108 | } 109 | esp_netif_dns_info_t dns; 110 | esp_netif_t *wifiSTA = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); 111 | if (esp_netif_get_dns_info(wifiSTA, ESP_NETIF_DNS_MAIN, &dns) == ESP_OK) 112 | { 113 | currentDNS = malloc(16); 114 | sprintf(currentDNS, IPSTR, IP2STR(&(dns.ip.u_addr.ip4))); 115 | ESP_LOGI(TAG, "Current DNS is: %s", currentDNS); 116 | } 117 | 118 | char *customDNS = NULL; 119 | get_config_param_str("custom_dns", &customDNS); 120 | 121 | if (customDNS == NULL) 122 | { 123 | defCB = "checked"; 124 | } 125 | else if (strcmp(customDNS, "1.1.1.1") == 0) 126 | { 127 | cloudCB = "checked"; 128 | } 129 | else if (strcmp(customDNS, "94.140.14.14") == 0) 130 | { 131 | adguardCB = "checked"; 132 | } 133 | else 134 | { 135 | customCB = "checked"; 136 | get_config_param_str("custom_dns", &customDNSIP); 137 | } 138 | 139 | uint8_t base_mac_addr[6] = {0}; 140 | uint8_t default_mac_addr[6] = {0}; 141 | ESP_ERROR_CHECK(esp_base_mac_addr_get(base_mac_addr)); 142 | ESP_ERROR_CHECK(esp_efuse_mac_get_default(default_mac_addr)); 143 | 144 | sprintf(currentMAC, "%x:%x:%x:%x:%x:%x", base_mac_addr[0], base_mac_addr[1], base_mac_addr[2], base_mac_addr[3], base_mac_addr[4], base_mac_addr[5]); 145 | sprintf(defaultMAC, "%x:%x:%x:%x:%x:%x", default_mac_addr[0], default_mac_addr[1], default_mac_addr[2], default_mac_addr[3], default_mac_addr[4], default_mac_addr[5]); 146 | 147 | get_config_param_str("custom_mac", &macSetting); 148 | 149 | if (strcmp(macSetting, "random") == 0) 150 | { 151 | rndMacCB = "checked"; 152 | } 153 | 154 | else if (strcmp(currentMAC, defaultMAC) == 0) 155 | { 156 | defMacCB = "checked"; 157 | } 158 | else 159 | { 160 | customMacCB = "checked"; 161 | customMac = currentMAC; 162 | } 163 | 164 | char *netmask = getNetmask(); 165 | 166 | if (strcmp(netmask, DEFAULT_NETMASK_CLASS_A) == 0) 167 | { 168 | classACB = "checked"; 169 | } 170 | 171 | else if (strcmp(netmask, DEFAULT_NETMASK_CLASS_B) == 0) 172 | { 173 | classBCB = "checked"; 174 | } 175 | else if (strcmp(netmask, DEFAULT_NETMASK_CLASS_C) == 0) 176 | { 177 | classCCB = "checked"; 178 | } 179 | else 180 | { 181 | customMaskCB = "checked"; 182 | customMask = netmask; 183 | } 184 | 185 | u_int size = advanced_html_size + strlen(aliveCB) + strlen(ledCB) + strlen(natCB) + strlen(currentDNS) + strlen(currentMAC) + 3 * strlen("checked") + strlen(customDNSIP) + 2 * strlen(defaultMAC) + strlen(customMac) + strlen(netmask) + strlen(hostName) + 2 * strlen("selected") + strlen(customMask) + 4 /* 4 * Octet - 4 *%d*/; 186 | ESP_LOGI(TAG, "Allocating additional %d bytes for advanced page.", size); 187 | char *advanced_page = malloc(size); 188 | 189 | char *subMac = malloc(strlen(defaultMAC) + 1); 190 | strcpy(subMac, defaultMAC); 191 | 192 | subMac[strlen(subMac) - 2] = '\0'; 193 | 194 | sprintf(advanced_page, advanced_start, hostName, octet, lowSelected, mediumSelected, highSelected, bwHigh, bwLow, ledCB, aliveCB, natCB, currentDNS, defCB, cloudCB, adguardCB, customCB, customDNSIP, currentMAC, defMacCB, defaultMAC, rndMacCB, subMac, customMacCB, customMac, netmask, classCCB, octet, classBCB, octet, classACB, octet, customMaskCB, customMask); 195 | 196 | closeHeader(req); 197 | esp_err_t ret = httpd_resp_send(req, advanced_page, HTTPD_RESP_USE_STRLEN); 198 | 199 | free(advanced_page); 200 | free(subMac); 201 | free(currentDNS); 202 | 203 | return ret; 204 | } 205 | -------------------------------------------------------------------------------- /src/urihandler/applyhandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include "timer.h" 3 | #include "helper.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "nvs.h" 9 | #include "cmd_nvs.h" 10 | #include "router_globals.h" 11 | #include "esp_wifi.h" 12 | 13 | static const char *TAG = "ApplyHandler"; 14 | 15 | void setApByQuery(char *urlContent, nvs_handle_t nvs) 16 | { 17 | size_t contentLength = 600; //passwords are max 64 characters, but special characters (i.e € = 9 character) are a lot more url encoded 18 | char param[contentLength]; 19 | readUrlParameterIntoBuffer(urlContent, "ap_ssid", param, contentLength); 20 | ESP_ERROR_CHECK(nvs_set_str(nvs, "ap_ssid", param)); 21 | readUrlParameterIntoBuffer(urlContent, "ap_password", param, contentLength); 22 | if (strlen(param) < 8) 23 | { 24 | nvs_erase_key(nvs, "ap_passwd"); 25 | } 26 | else 27 | { 28 | ESP_ERROR_CHECK(nvs_set_str(nvs, "ap_passwd", param)); 29 | } 30 | 31 | readUrlParameterIntoBuffer(urlContent, "ssid_hidden", param, contentLength); 32 | if (strcmp(param, "on") == 0) 33 | { 34 | ESP_LOGI(TAG, "AP-SSID should be hidden."); 35 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "ssid_hidden", 1)); 36 | } 37 | else 38 | { 39 | nvs_erase_key(nvs, "ssid_hidden"); 40 | } 41 | } 42 | 43 | void setStaByQuery(char *urlContent, nvs_handle_t nvs) 44 | { 45 | 46 | size_t contentLength = 600; 47 | char param[contentLength]; 48 | readUrlParameterIntoBuffer(urlContent, "ssid", param, contentLength); 49 | ESP_ERROR_CHECK(nvs_set_str(nvs, "ssid", param)); 50 | readUrlParameterIntoBuffer(urlContent, "password", param, contentLength); 51 | ESP_ERROR_CHECK(nvs_set_str(nvs, "passwd", param)); 52 | } 53 | void setWpa2(char *urlContent, nvs_handle_t nvs) 54 | { 55 | size_t contentLength = strlen(urlContent); 56 | char param[contentLength]; 57 | readUrlParameterIntoBuffer(urlContent, "sta_identity", param, contentLength); 58 | if (strlen(param) > 0) 59 | { 60 | ESP_LOGI(TAG, "WPA2 Identity set to '%s'", param); 61 | ESP_ERROR_CHECK(nvs_set_str(nvs, "sta_identity", param)); 62 | } 63 | else 64 | { 65 | ESP_LOGI(TAG, "WPA2 Identity will be deleted"); 66 | nvs_erase_key(nvs, "sta_identity"); 67 | } 68 | 69 | readUrlParameterIntoBuffer(urlContent, "sta_user", param, contentLength); 70 | 71 | if (strlen(param) > 0) 72 | { 73 | ESP_LOGI(TAG, "WPA2 user set to '%s'", param); 74 | ESP_ERROR_CHECK(nvs_set_str(nvs, "sta_user", param)); 75 | } 76 | else 77 | { 78 | ESP_LOGI(TAG, "WPA2 user will be deleted"); 79 | nvs_erase_key(nvs, "sta_user"); 80 | } 81 | readUrlParameterIntoBuffer(urlContent, "cer", param, contentLength); 82 | 83 | if (strlen(param) > 0) 84 | { 85 | nvs_erase_key(nvs, "cer"); // do not double size in nvs 86 | ESP_LOGI(TAG, "Certificate with size %d set", strlen(param)); 87 | ESP_ERROR_CHECK(nvs_set_blob(nvs, "cer", param, contentLength)); 88 | } 89 | else 90 | { 91 | ESP_LOGI(TAG, "Certificate will be deleted"); 92 | nvs_erase_key(nvs, "cer"); 93 | } 94 | } 95 | 96 | void applyApStaConfig(char *buf) 97 | { 98 | nvs_handle_t nvs; 99 | ESP_ERROR_CHECK(nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs)); 100 | setApByQuery(buf, nvs); 101 | setStaByQuery(buf, nvs); 102 | setWpa2(buf, nvs); 103 | ESP_ERROR_CHECK(nvs_commit(nvs)); 104 | nvs_close(nvs); 105 | } 106 | 107 | void eraseNvs() 108 | { 109 | ESP_LOGW(TAG, "Erasing %s", PARAM_NAMESPACE); 110 | int argc = 2; 111 | char *argv[argc]; 112 | argv[0] = "erase_namespace"; 113 | argv[1] = PARAM_NAMESPACE; 114 | erase_ns(argc, argv); 115 | } 116 | 117 | void setDNSToDefault(nvs_handle_t *nvs) 118 | { 119 | nvs_erase_key(*nvs, "custom_dns"); 120 | ESP_LOGI(TAG, "DNS set to default (uplink network)"); 121 | } 122 | 123 | void setMACToDefault(nvs_handle_t *nvs) 124 | { 125 | nvs_erase_key(*nvs, "custom_mac"); 126 | ESP_LOGI(TAG, "MAC set to default"); 127 | } 128 | 129 | bool str2mac(const char *mac) 130 | { 131 | uint8_t values[6] = {0}; 132 | if (6 == sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1], &values[2], &values[3], &values[4], &values[5])) 133 | { 134 | return true; 135 | } 136 | else 137 | { 138 | return false; 139 | } 140 | } 141 | 142 | char *getRedirectUrl(httpd_req_t *req) 143 | { 144 | 145 | size_t buf_len = 16; 146 | char *host = malloc(buf_len); 147 | httpd_req_get_hdr_value_str(req, "Host", host, buf_len); 148 | ESP_LOGI(TAG, "Host of request is '%s'", host); 149 | char *str = malloc(strlen("http://") + buf_len); 150 | strcpy(str, "http://"); 151 | if (strcmp(host, DEFAULT_AP_IP_CLASS_A) == 0 || strcmp(host, DEFAULT_AP_IP_CLASS_B) == 0 || strcmp(host, DEFAULT_AP_IP_CLASS_C) == 0) 152 | { 153 | char *defaultIP = getDefaultIPByNetmask(); 154 | strcat(str, defaultIP); 155 | free(defaultIP); 156 | } 157 | else 158 | { 159 | strcat(str, host); 160 | } 161 | free(host); 162 | 163 | return str; 164 | } 165 | 166 | void applyAdvancedConfig(char *buf) 167 | { 168 | ESP_LOGI(TAG, "Applying advanced config"); 169 | nvs_handle_t nvs; 170 | nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs); 171 | 172 | size_t contentLength = 250; 173 | char param[contentLength]; 174 | readUrlParameterIntoBuffer(buf, "keepalive", param, contentLength); 175 | 176 | if (strlen(param) > 0) 177 | { 178 | ESP_LOGI(TAG, "keep alive will be enabled"); 179 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "keep_alive", 1)); 180 | } 181 | else 182 | { 183 | ESP_LOGI(TAG, "keep alive will be disabled"); 184 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "keep_alive", 0)); 185 | } 186 | 187 | readUrlParameterIntoBuffer(buf, "ledenabled", param, contentLength); 188 | if (strlen(param) > 0) 189 | { 190 | ESP_LOGI(TAG, "ON Board LED will be enabled"); 191 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "led_disabled", 0)); 192 | } 193 | else 194 | { 195 | ESP_LOGI(TAG, "ON Board LED will be disabled"); 196 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "led_disabled", 1)); 197 | } 198 | 199 | readUrlParameterIntoBuffer(buf, "natenabled", param, contentLength); 200 | if (strlen(param) > 0) 201 | { 202 | ESP_LOGI(TAG, "NAT will be enabled"); 203 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "nat_disabled", 0)); 204 | } 205 | else 206 | { 207 | ESP_LOGI(TAG, "NAT will be disabled"); 208 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "nat_disabled", 1)); 209 | } 210 | 211 | readUrlParameterIntoBuffer(buf, "wsenabled", param, contentLength); 212 | if (strlen(param) == 0) 213 | { 214 | ESP_LOGI(TAG, "Webserver will be disabled"); 215 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "lock", 1)); 216 | } 217 | 218 | readUrlParameterIntoBuffer(buf, "custommac", param, contentLength); 219 | if (strlen(param) > 0) 220 | { 221 | char macaddress[contentLength]; 222 | readUrlParameterIntoBuffer(buf, "macaddress", macaddress, contentLength); 223 | if (strcmp("random", param) == 0) 224 | { 225 | ESP_LOGI(TAG, "MAC address set to random"); 226 | ESP_ERROR_CHECK(nvs_set_str(nvs, "custom_mac", param)); 227 | } 228 | else if (strlen(macaddress) > 0) 229 | { 230 | int success = str2mac(macaddress); 231 | if (success) 232 | { 233 | ESP_LOGI(TAG, "MAC address set to: %s", macaddress); 234 | ESP_ERROR_CHECK(nvs_set_str(nvs, "custom_mac", macaddress)); 235 | } 236 | else 237 | { 238 | ESP_LOGI(TAG, "MAC address '%s' is invalid", macaddress); 239 | setMACToDefault(&nvs); 240 | } 241 | } 242 | else 243 | { 244 | setMACToDefault(&nvs); 245 | } 246 | } 247 | readUrlParameterIntoBuffer(buf, "dns", param, contentLength); 248 | if (strlen(param) > 0) 249 | { 250 | if (strcmp(param, "custom") == 0) 251 | { 252 | char customDnsParam[contentLength]; 253 | readUrlParameterIntoBuffer(buf, "dnsip", customDnsParam, contentLength); 254 | if (strlen(customDnsParam) > 0) 255 | { 256 | uint32_t ipasInt = esp_ip4addr_aton(customDnsParam); 257 | if (ipasInt == UINT32_MAX || ipasInt == 0) 258 | { 259 | ESP_LOGW(TAG, "Invalid custom DNS. Setting back to default!"); 260 | setDNSToDefault(&nvs); 261 | } 262 | else 263 | { 264 | esp_ip4_addr_t *addr = malloc(16); 265 | addr->addr = ipasInt; 266 | esp_ip4addr_ntoa(addr, customDnsParam, 16); 267 | ESP_LOGI(TAG, "DNS set to: %s", customDnsParam); 268 | ESP_ERROR_CHECK(nvs_set_str(nvs, "custom_dns", customDnsParam)); 269 | } 270 | } 271 | else 272 | { 273 | setDNSToDefault(&nvs); 274 | } 275 | } 276 | else 277 | { 278 | ESP_LOGI(TAG, "DNS set to: %s", param); 279 | ESP_ERROR_CHECK(nvs_set_str(nvs, "custom_dns", param)); 280 | } 281 | } 282 | else 283 | { 284 | setDNSToDefault(&nvs); 285 | } 286 | readUrlParameterIntoBuffer(buf, "netmask", param, contentLength); 287 | if (strlen(param) > 0) 288 | { 289 | if (strcmp("classa", param) == 0) 290 | { 291 | ESP_LOGI(TAG, "Netmask set to Class A"); 292 | ESP_ERROR_CHECK(nvs_set_str(nvs, "netmask", DEFAULT_NETMASK_CLASS_A)); 293 | } 294 | else if (strcmp("classb", param) == 0) 295 | { 296 | ESP_LOGI(TAG, "Netmask set to Class B"); 297 | ESP_ERROR_CHECK(nvs_set_str(nvs, "netmask", DEFAULT_NETMASK_CLASS_B)); 298 | } 299 | else if (strcmp("classc", param) == 0) 300 | { 301 | ESP_LOGI(TAG, "Netmask set to Class C"); 302 | ESP_ERROR_CHECK(nvs_set_str(nvs, "netmask", DEFAULT_NETMASK_CLASS_C)); 303 | } 304 | else 305 | { 306 | readUrlParameterIntoBuffer(buf, "mask", param, contentLength); 307 | if (is_valid_subnet_mask(param)) 308 | { 309 | ESP_LOGI(TAG, "Netmask set to %s", param); 310 | ESP_ERROR_CHECK(nvs_set_str(nvs, "netmask", param)); 311 | } 312 | else 313 | { 314 | ESP_LOGW(TAG, "Invalid custom subnetmask. Setting to default."); 315 | ESP_ERROR_CHECK(nvs_set_str(nvs, "netmask", DEFAULT_NETMASK_CLASS_C)); 316 | } 317 | } 318 | } 319 | readUrlParameterIntoBuffer(buf, "hostname", param, contentLength); 320 | if (strlen(param) > 0) 321 | { 322 | ESP_LOGI(TAG, "Set hostname to: %s", param); 323 | ESP_ERROR_CHECK(nvs_set_str(nvs, "hostname", param)); 324 | } 325 | else 326 | { 327 | ESP_LOGI(TAG, "Erasing hostname. Will be regenerated on boot."); 328 | nvs_erase_key(nvs, "hostname"); 329 | } 330 | 331 | readUrlParameterIntoBuffer(buf, "octet", param, contentLength); 332 | int octet = atoi(param); 333 | if (strlen(param) > 0 && octet >= 0 && octet <= 255) 334 | { 335 | ESP_LOGI(TAG, "Set third octet to: %d", octet); 336 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "octet", octet)); 337 | } 338 | else 339 | { 340 | ESP_LOGW(TAG, "Invalid octet parameter. Will be erased"); 341 | nvs_erase_key(nvs, "octet"); 342 | } 343 | readUrlParameterIntoBuffer(buf, "txpower", param, contentLength); 344 | int txPower = atoi(param); 345 | if (txPower >= 8 && txPower <= 84) 346 | { 347 | ESP_LOGI(TAG, "Setting Wifi tx power to %d.", txPower); 348 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "txpower", txPower)); 349 | } 350 | readUrlParameterIntoBuffer(buf, "bandwith", param, contentLength); 351 | int useLowerBandwith = atoi(param); 352 | if (useLowerBandwith == 1) 353 | { 354 | ESP_LOGI(TAG, "Using lower bandwith with 40 MHz"); 355 | ESP_ERROR_CHECK(nvs_set_i32(nvs, "lower_bandwith", 1)); 356 | } 357 | else 358 | { 359 | nvs_erase_key(nvs, "lower_bandwith"); 360 | } 361 | 362 | nvs_commit(nvs); 363 | nvs_close(nvs); 364 | } 365 | 366 | esp_err_t apply_get_handler(httpd_req_t *req) 367 | { 368 | 369 | if (isLocked()) 370 | { 371 | return redirectToLock(req); 372 | } 373 | extern const char apply_start[] asm("_binary_apply_html_start"); 374 | extern const char apply_end[] asm("_binary_apply_html_end"); 375 | ESP_LOGI(TAG, "Requesting apply page"); 376 | closeHeader(req); 377 | 378 | char *redirectUrl = getRedirectUrl(req); 379 | char *apply_page = malloc(apply_end - apply_start + strlen(redirectUrl) - 2); 380 | 381 | ESP_LOGI(TAG, "Redirecting after apply to '%s'", redirectUrl); 382 | sprintf(apply_page, apply_start, redirectUrl); 383 | free(redirectUrl); 384 | 385 | return httpd_resp_send(req, apply_page, HTTPD_RESP_USE_STRLEN); 386 | } 387 | esp_err_t apply_post_handler(httpd_req_t *req) 388 | { 389 | if (isLocked()) 390 | { 391 | return redirectToLock(req); 392 | } 393 | httpd_req_to_sockfd(req); 394 | 395 | int remaining = req->content_len; 396 | int ret = 0; 397 | int bufferLength = req->content_len; 398 | ESP_LOGI(TAG, "Content length => %d", req->content_len); 399 | char buf[100]; // 1000 byte chunk 400 | char content[bufferLength]; 401 | strcpy(content, ""); // Fill initial 402 | 403 | while (remaining > 0) 404 | { 405 | /* Read the data for the request */ 406 | if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) 407 | { 408 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) 409 | { 410 | continue; 411 | } 412 | ESP_LOGE(TAG, "Timeout occured %d", ret); 413 | return ESP_FAIL; 414 | } 415 | buf[ret] = '\0'; // add NUL terminator 416 | strcat(content, buf); 417 | remaining -= ret; 418 | ESP_LOGI(TAG, "%d bytes total received -> %d left", strlen(content), remaining); 419 | } 420 | char funcParam[9]; 421 | 422 | ESP_LOGI(TAG, "getting content %s", content); 423 | 424 | readUrlParameterIntoBuffer(content, "func", funcParam, 9); 425 | 426 | ESP_LOGI(TAG, "Function => %s", funcParam); 427 | 428 | if (strcmp(funcParam, "config") == 0) 429 | { 430 | applyApStaConfig(content); 431 | } 432 | if (strcmp(funcParam, "erase") == 0) 433 | { 434 | eraseNvs(); 435 | } 436 | if (strcmp(funcParam, "advanced") == 0) 437 | { 438 | applyAdvancedConfig(content); 439 | } 440 | restartByTimerinS(1); 441 | 442 | return apply_get_handler(req); 443 | } -------------------------------------------------------------------------------- /src/urihandler/clientshandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include "timer.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "nvs.h" 8 | #include "cmd_nvs.h" 9 | #include "router_globals.h" 10 | #include "esp_wifi.h" 11 | #include "esp_wifi_ap_get_sta_list.h" 12 | 13 | static const char *TAG = "ClientsHandler"; 14 | 15 | const char *CLIENT_TEMPLATE = "%i%s%s"; 16 | 17 | esp_err_t clients_download_get_handler(httpd_req_t *req) 18 | { 19 | if (isLocked()) 20 | { 21 | return redirectToLock(req); 22 | } 23 | 24 | wifi_sta_list_t wifi_sta_list; 25 | wifi_sta_mac_ip_list_t adapter_sta_list; 26 | memset(&wifi_sta_list, 0, sizeof(wifi_sta_list)); 27 | memset(&adapter_sta_list, 0, sizeof(adapter_sta_list)); 28 | esp_wifi_ap_get_sta_list(&wifi_sta_list); 29 | 30 | esp_wifi_ap_get_sta_list_with_ip(&wifi_sta_list, &adapter_sta_list); 31 | 32 | char result[1000]; 33 | strcpy(result, ""); 34 | if (wifi_sta_list.num > 0) 35 | { 36 | for (int i = 0; i < adapter_sta_list.num; i++) 37 | { 38 | 39 | char *template = malloc(strlen(CLIENT_TEMPLATE) + 100); 40 | esp_netif_pair_mac_ip_t station = adapter_sta_list.sta[i]; 41 | 42 | char str_ip[16]; 43 | esp_ip4addr_ntoa(&(station.ip), str_ip, IP4ADDR_STRLEN_MAX); 44 | 45 | char currentMAC[18]; 46 | sprintf(currentMAC, "%x:%x:%x:%x:%x:%x", station.mac[0], station.mac[1], station.mac[2], station.mac[3], station.mac[4], station.mac[5]); 47 | 48 | sprintf(template, CLIENT_TEMPLATE, i + 1, str_ip, currentMAC); 49 | strcat(result, template); 50 | } 51 | } 52 | else 53 | { 54 | strcat(result, "No clients connected"); 55 | } 56 | 57 | httpd_req_to_sockfd(req); 58 | extern const char clients_start[] asm("_binary_clients_html_start"); 59 | extern const char clients_end[] asm("_binary_clients_html_end"); 60 | const size_t clients_html_size = (clients_end - clients_start); 61 | 62 | int size = clients_html_size + strlen(result); 63 | char *clients_page = malloc(size - 2); 64 | sprintf(clients_page, clients_start, result); 65 | 66 | closeHeader(req); 67 | 68 | esp_err_t ret = httpd_resp_send(req, clients_page, HTTPD_RESP_USE_STRLEN); 69 | free(clients_page); 70 | ESP_LOGI(TAG, "Requesting clients page"); 71 | return ret; 72 | } 73 | -------------------------------------------------------------------------------- /src/urihandler/handler.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "router_globals.h" 4 | #include "lwip/ip4_addr.h" 5 | #include "helper.h" 6 | #include "cmd_system.h" 7 | 8 | /* Static */ 9 | void closeHeader(httpd_req_t *req); 10 | esp_err_t styles_download_get_handler(httpd_req_t *req); 11 | esp_err_t jquery_get_handler(httpd_req_t *req); 12 | esp_err_t favicon_get_handler(httpd_req_t *req); 13 | esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err); 14 | esp_err_t redirectToRoot(httpd_req_t *req); 15 | esp_err_t reset_get_handler(httpd_req_t *req); 16 | 17 | /* IndexHandler */ 18 | esp_err_t index_get_handler(httpd_req_t *req); 19 | esp_err_t index_post_handler(httpd_req_t *req); 20 | 21 | /* Lockhandler */ 22 | bool isLocked(); 23 | void lockUI(); 24 | esp_err_t unlock_handler(httpd_req_t *req); 25 | esp_err_t lock_handler(httpd_req_t *req); 26 | esp_err_t redirectToLock(httpd_req_t *req); 27 | 28 | /* ScanHandler */ 29 | void fillInfoData(char **db, char **textColor); 30 | esp_err_t scan_download_get_handler(httpd_req_t *req); 31 | 32 | /* ResultHandler */ 33 | esp_err_t result_download_get_handler(httpd_req_t *req); 34 | 35 | /* ApplyHandler */ 36 | esp_err_t apply_get_handler(httpd_req_t *req); 37 | esp_err_t apply_post_handler(httpd_req_t *req); 38 | 39 | // /* RestHandler */ 40 | esp_err_t rest_handler(httpd_req_t *req); 41 | 42 | /* advanced handler */ 43 | esp_err_t advanced_download_get_handler(httpd_req_t *req); 44 | 45 | /* clients handler*/ 46 | esp_err_t clients_download_get_handler(httpd_req_t *req); 47 | 48 | /* OTA */ 49 | esp_err_t ota_download_get_handler(httpd_req_t *req); 50 | esp_err_t otalog_get_handler(httpd_req_t *req); 51 | esp_err_t ota_post_handler(httpd_req_t *req); 52 | esp_err_t otalog_post_handler(httpd_req_t *req); 53 | 54 | /* About-Handler */ 55 | esp_err_t about_get_handler(httpd_req_t *req); 56 | 57 | /* Portmap -Handler*/ 58 | esp_err_t portmap_get_handler(httpd_req_t *req); 59 | esp_err_t portmap_post_handler(httpd_req_t *req); -------------------------------------------------------------------------------- /src/urihandler/helper.c: -------------------------------------------------------------------------------- 1 | #include "helper.h" 2 | #include 3 | 4 | static const char *TAG = "urihelper"; 5 | 6 | void preprocess_string(char *str) 7 | { 8 | char *p, *q; 9 | 10 | for (p = q = str; *p != 0; p++) 11 | { 12 | if (*(p) == '%' && *(p + 1) != 0 && *(p + 2) != 0) 13 | { 14 | // quoted hex 15 | uint8_t a; 16 | p++; 17 | if (*p <= '9') 18 | a = *p - '0'; 19 | else 20 | a = toupper((unsigned char)*p) - 'A' + 10; 21 | a <<= 4; 22 | p++; 23 | if (*p <= '9') 24 | a += *p - '0'; 25 | else 26 | a += toupper((unsigned char)*p) - 'A' + 10; 27 | *q++ = a; 28 | } 29 | else if (*(p) == '+') 30 | { 31 | *q++ = ' '; 32 | } 33 | else 34 | { 35 | *q++ = *p; 36 | } 37 | } 38 | *q = '\0'; 39 | } 40 | 41 | void readUrlParameterIntoBuffer(char *parameterString, char *parameter, char *buffer, size_t paramLength) 42 | { 43 | if (httpd_query_key_value(parameterString, parameter, buffer, paramLength) == ESP_OK) 44 | { 45 | preprocess_string(buffer); 46 | ESP_LOGI(TAG, "Found '%s' parameter => %s", parameter, buffer); 47 | } 48 | else 49 | { 50 | ESP_LOGI(TAG, "Parameter '%s' not found", parameter); 51 | buffer[0] = '\0'; 52 | } 53 | } 54 | 55 | esp_err_t fill_post_buffer(httpd_req_t *req, char *buf, size_t len) 56 | { 57 | int ret, remaining = len; 58 | 59 | while (remaining > 0) 60 | { 61 | /* Read the data for the request */ 62 | if ((ret = httpd_req_recv(req, buf, MIN(remaining, len))) <= 0) 63 | { 64 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) 65 | { 66 | continue; 67 | } 68 | ESP_LOGE(TAG, "Timeout occurred"); 69 | return ESP_FAIL; 70 | } 71 | 72 | remaining -= ret; 73 | } 74 | 75 | return ESP_OK; 76 | } 77 | 78 | bool is_valid_subnet_mask(char *subnet_mask) 79 | { 80 | char *token; 81 | int octet; 82 | int count = 0; 83 | 84 | // ip_addr_t ip_addr; 85 | // if (ipaddr_aton(subnet_mask, &ip_addr)) 86 | // { 87 | // // IP-Adresse wurde erfolgreich umgewandelt 88 | 89 | // // Schleife, um jedes Bit auszugeben 90 | // char buf[65]; 91 | // u_int32_t num = ip4_addr_get_u32(ip_2_ip4(&ip_addr)); 92 | // // Ausgabe in binärer Form mit dem ESP-IDF-Logframework 93 | // ESP_LOGW(TAG, "Binäre Darstellung von %lu: %s", num, utoa(num, buf, 2)); 94 | // } 95 | // else 96 | // { 97 | // return false; 98 | // } 99 | 100 | // Copy for calculation 101 | char mask_copy[strlen(subnet_mask) + 1]; 102 | strcpy(mask_copy, subnet_mask); 103 | 104 | // split by dots 105 | token = strtok(mask_copy, "."); 106 | 107 | while (token != NULL) 108 | { 109 | // Convert to int 110 | octet = atoi(token); 111 | // Valid value between 0 and 255 112 | if (octet < 0 || octet > 255) 113 | { 114 | ESP_LOGE(TAG, "%s is not a valid subnet mask. Octet %d out of range.", subnet_mask, octet); 115 | return false; 116 | } 117 | 118 | // Count octetts 119 | count++; 120 | 121 | token = strtok(NULL, "."); 122 | } 123 | 124 | // Exactly 4 octetts 125 | if (count != 4) 126 | { 127 | ESP_LOGE(TAG, "%s is not a valid subnet mask. Not exactly 4 octets.", subnet_mask); 128 | return false; 129 | } 130 | 131 | // Check bits. Every bit other the last 1 must be 0 132 | unsigned int mask_value = 0; 133 | strcpy(mask_copy, subnet_mask); 134 | token = strtok(mask_copy, "."); 135 | while (token != NULL) 136 | { 137 | octet = atoi(token); 138 | mask_value = (mask_value << 8) | octet; 139 | token = strtok(NULL, "."); 140 | } 141 | unsigned int inverted_mask = ~mask_value; 142 | if ((inverted_mask & (inverted_mask + 1)) != 0) 143 | { 144 | ESP_LOGE(TAG, "%s is not a valid subnet mask. The bits after the last 1 have to be zero.", subnet_mask); 145 | return false; 146 | } 147 | 148 | return true; 149 | } -------------------------------------------------------------------------------- /src/urihandler/helper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void preprocess_string(char *str); 7 | void readUrlParameterIntoBuffer(char *parameterString, char *parameter, char *buffer, size_t paramLength); 8 | esp_err_t fill_post_buffer(httpd_req_t *req, char *buf, size_t len); 9 | bool is_valid_subnet_mask(char *subnet_mask); -------------------------------------------------------------------------------- /src/urihandler/indexhandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include 3 | #include "router_globals.h" 4 | 5 | static const char *TAG = "IndexHandler"; 6 | 7 | char *appliedSSID = NULL; 8 | 9 | bool isWrongHost(httpd_req_t *req) 10 | { 11 | char *currentIP = getDefaultIPByNetmask(); 12 | size_t buf_len = strlen(currentIP) + 1; 13 | char *host = malloc(buf_len); 14 | httpd_req_get_hdr_value_str(req, "Host", host, buf_len); 15 | bool out = strcmp(host, currentIP) != 0; 16 | free(host); 17 | free(currentIP); 18 | return out; 19 | } 20 | 21 | esp_err_t index_get_handler(httpd_req_t *req) 22 | { 23 | if (isWrongHost(req) && isDnsStarted()) 24 | { 25 | ESP_LOGI(TAG, "Captive portal redirect"); 26 | return redirectToRoot(req); 27 | } 28 | 29 | if (isLocked()) 30 | { 31 | return redirectToLock(req); 32 | } 33 | char *result_param = NULL; 34 | char *displayResult = "none"; 35 | 36 | char *scanButtonWidth = "12"; 37 | get_config_param_str("scan_result", &result_param); 38 | int32_t result_shown = 0; 39 | get_config_param_int("result_shown", &result_shown); 40 | if (result_param != NULL && result_shown < 3) 41 | { 42 | if (result_shown == 0) 43 | { 44 | ESP_LOGI(TAG, "Scan result is available and not shown already. Forwarding to scan page"); 45 | httpd_resp_set_status(req, "302 Found"); 46 | httpd_resp_set_hdr(req, "Location", "/result"); 47 | return httpd_resp_send(req, NULL, 0); 48 | } 49 | 50 | scanButtonWidth = "9"; 51 | displayResult = "block"; 52 | } 53 | 54 | httpd_req_to_sockfd(req); 55 | extern const char config_start[] asm("_binary_config_html_start"); 56 | extern const char config_end[] asm("_binary_config_html_end"); 57 | const size_t config_html_size = (config_end - config_start); 58 | 59 | char *displayLockButton = NULL; 60 | char *displayRelockButton = NULL; 61 | 62 | char *lock_pass = NULL; 63 | get_config_param_str("lock_pass", &lock_pass); 64 | if (lock_pass != NULL && strlen(lock_pass) > 0) 65 | { 66 | displayLockButton = "none"; 67 | displayRelockButton = "flex"; 68 | } 69 | else 70 | { 71 | displayLockButton = "block"; 72 | displayRelockButton = "none"; 73 | } 74 | 75 | int32_t ssidHidden = 0; 76 | char *hiddenSSID = NULL; 77 | get_config_param_int("ssid_hidden", &ssidHidden); 78 | if (ssidHidden == 1) 79 | { 80 | hiddenSSID = "checked"; 81 | } 82 | else 83 | { 84 | hiddenSSID = ""; 85 | } 86 | 87 | size_t size = strlen(ap_ssid) + strlen(ap_passwd) + strlen(displayLockButton); 88 | if (appliedSSID != NULL && strlen(appliedSSID) > 0) 89 | { 90 | size = size + strlen(appliedSSID); 91 | } 92 | else 93 | { 94 | size = size + strlen(ssid) + strlen(passwd); 95 | } 96 | char *db = NULL; 97 | char *textColor = NULL; 98 | char *wifiOn, *wifiOff = NULL; 99 | fillInfoData(&db, &textColor); 100 | if (strcmp(db, "0") == 0) 101 | { 102 | wifiOn = "none"; 103 | wifiOff = "inline-block"; 104 | } 105 | else 106 | { 107 | wifiOn = "inline-block"; 108 | wifiOff = "none"; 109 | } 110 | 111 | size = size + +strlen(wifiOn) + strlen(wifiOff) + strlen(textColor) + 5 /* Länge der clients */ + strlen(db); 112 | /* WPA2 */ 113 | char *wpa2CB = NULL; 114 | char *wpa2Input = NULL; 115 | char *sta_identity = NULL; 116 | char *sta_user = NULL; 117 | size_t len = 0; 118 | 119 | char *cert = NULL; 120 | get_config_param_str("sta_identity", &sta_identity); 121 | get_config_param_str("sta_user", &sta_user); 122 | 123 | get_config_param_blob("cer", &cert, &len); 124 | char *cer = NULL; 125 | if (len > 0) 126 | { 127 | cer = (char *)malloc(len + 1 * sizeof(char)); 128 | strncpy(cer, cert, len + 1); 129 | } 130 | if ((sta_identity != NULL && strlen(sta_identity) != 0) || (sta_user != NULL && strlen(sta_user) != 0)) 131 | { 132 | wpa2CB = "checked"; 133 | wpa2Input = "block"; 134 | if (sta_identity == NULL) 135 | { 136 | sta_identity = ""; 137 | } 138 | if (sta_user == NULL) 139 | { 140 | sta_user = ""; 141 | } 142 | if (cer == NULL) 143 | { 144 | cer = ""; 145 | } 146 | } 147 | else 148 | { 149 | wpa2CB = ""; 150 | wpa2Input = "none"; 151 | sta_identity = ""; 152 | sta_user = ""; 153 | cer = ""; 154 | } 155 | 156 | size = size + strlen(wpa2CB) + strlen(wpa2Input) + strlen(sta_identity) + strlen(sta_user) + strlen(cer) + strlen(displayResult) + strlen(scanButtonWidth) + strlen(displayLockButton) + strlen(displayRelockButton); 157 | ESP_LOGI(TAG, "Allocating additional %d bytes for config page.", config_html_size + size); 158 | 159 | char *config_page = malloc(config_html_size + size); 160 | uint16_t connect_count = getConnectCount(); 161 | 162 | if (appliedSSID != NULL && strlen(appliedSSID) > 0) 163 | { 164 | sprintf(config_page, config_start, connect_count, hiddenSSID, ap_ssid, ap_passwd, textColor, wifiOff, wifiOn, db, wpa2CB, appliedSSID, wpa2Input, sta_identity, sta_user, cer, "", scanButtonWidth, displayResult, displayLockButton, displayRelockButton); 165 | } 166 | else 167 | { 168 | sprintf(config_page, config_start, connect_count, hiddenSSID, ap_ssid, ap_passwd, textColor, wifiOff, wifiOn, db, wpa2CB, ssid, wpa2Input, sta_identity, sta_user, cer, passwd, scanButtonWidth, displayResult, displayLockButton, displayRelockButton); 169 | } 170 | 171 | closeHeader(req); 172 | 173 | esp_err_t ret = httpd_resp_send(req, config_page, HTTPD_RESP_USE_STRLEN); 174 | free(config_page); 175 | free(appliedSSID); 176 | appliedSSID = NULL; 177 | free(db); 178 | if (strlen(cer) > 0) // Error on C3 179 | { 180 | free(cer); 181 | } 182 | 183 | return ret; 184 | } 185 | 186 | esp_err_t index_post_handler(httpd_req_t *req) 187 | { 188 | 189 | if (isLocked()) 190 | { 191 | return redirectToLock(req); 192 | } 193 | httpd_req_to_sockfd(req); 194 | 195 | httpd_req_to_sockfd(req); 196 | 197 | size_t content_len = req->content_len; 198 | char buf[content_len]; 199 | 200 | if (fill_post_buffer(req, buf, content_len) == ESP_OK) 201 | { 202 | char ssidParam[req->content_len]; 203 | readUrlParameterIntoBuffer(buf, "ssid", ssidParam, req->content_len); 204 | 205 | if (strlen(ssidParam) > 0) 206 | { 207 | ESP_LOGI(TAG, "Found SSID parameter => %s", ssidParam); 208 | appliedSSID = malloc(strlen(ssidParam) + 1); 209 | strcpy(appliedSSID, ssidParam); 210 | } 211 | } 212 | httpd_resp_set_status(req, "302 Temporary Redirect"); 213 | httpd_resp_set_hdr(req, "Location", "/"); 214 | return httpd_resp_send(req, NULL, 0); 215 | } 216 | -------------------------------------------------------------------------------- /src/urihandler/lockhandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include "timer.h" 3 | 4 | #include 5 | 6 | #include "nvs.h" 7 | #include "router_globals.h" 8 | 9 | static const char *TAG = "LockHandler"; 10 | 11 | bool locked = false; 12 | 13 | bool isLocked() 14 | { 15 | return locked; 16 | } 17 | void lockUI() 18 | { 19 | locked = true; 20 | } 21 | esp_err_t unlock_handler(httpd_req_t *req) 22 | { 23 | 24 | httpd_req_to_sockfd(req); 25 | 26 | size_t content_len = req->content_len; 27 | char buf[content_len]; 28 | 29 | if (fill_post_buffer(req, buf, content_len) == ESP_OK) 30 | { 31 | 32 | char unlockParam[req->content_len]; 33 | readUrlParameterIntoBuffer(buf, "unlock", unlockParam, req->content_len); 34 | 35 | if (strlen(unlockParam) > 0) 36 | { 37 | char *lock; 38 | get_config_param_str("lock_pass", &lock); 39 | if (strcmp(lock, unlockParam) == 0) 40 | { 41 | locked = false; 42 | httpd_resp_set_status(req, "302 Found"); 43 | httpd_resp_set_hdr(req, "Location", "/"); 44 | return httpd_resp_send(req, NULL, 0); 45 | } 46 | } 47 | } 48 | if (req->method == HTTP_GET) // Relock if called 49 | { 50 | locked = true; 51 | ESP_LOGI(TAG, "UI relocked"); 52 | } 53 | extern const char ul_start[] asm("_binary_unlock_html_start"); 54 | 55 | closeHeader(req); 56 | return httpd_resp_send(req, ul_start, HTTPD_RESP_USE_STRLEN); 57 | } 58 | 59 | esp_err_t redirectToLock(httpd_req_t *req) 60 | { 61 | httpd_resp_set_status(req, "302 Found"); 62 | httpd_resp_set_hdr(req, "Location", "/unlock"); 63 | return httpd_resp_send(req, NULL, 0); 64 | } 65 | esp_err_t lock_handler(httpd_req_t *req) 66 | { 67 | if (locked) 68 | { 69 | return redirectToLock(req); 70 | } 71 | httpd_req_to_sockfd(req); 72 | 73 | if (req->method == HTTP_POST) // Relock if called 74 | { 75 | int ret, remaining = req->content_len; 76 | char buf[req->content_len]; 77 | 78 | while (remaining > 0) 79 | { 80 | /* Read the data for the request */ 81 | if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) 82 | { 83 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) 84 | { 85 | continue; 86 | } 87 | ESP_LOGE(TAG, "Timeout occured"); 88 | return ESP_FAIL; 89 | } 90 | 91 | remaining -= ret; 92 | } 93 | 94 | char passParam[req->content_len], pass2Param[req->content_len]; 95 | 96 | readUrlParameterIntoBuffer(buf, "lockpass", passParam, req->content_len); 97 | readUrlParameterIntoBuffer(buf, "lockpass2", pass2Param, req->content_len); 98 | ESP_LOGI(TAG, "Found pass2 parameter => %s", pass2Param); 99 | if (strlen(passParam) == strlen(pass2Param) && strcmp(passParam, pass2Param) == 0) 100 | { 101 | ESP_LOGI(TAG, "Passes are equal. Password will be changed."); 102 | if (strlen(passParam) == 0) 103 | { 104 | ESP_LOGI(TAG, "Pass will be removed"); 105 | } 106 | nvs_handle_t nvs; 107 | nvs_open(PARAM_NAMESPACE, NVS_READWRITE, &nvs); 108 | nvs_set_str(nvs, "lock_pass", passParam); 109 | nvs_commit(nvs); 110 | nvs_close(nvs); 111 | httpd_resp_set_status(req, "302 Found"); 112 | if (strlen(passParam) > 0) 113 | { 114 | httpd_resp_set_hdr(req, "Location", "/lock"); 115 | lockUI(); 116 | } 117 | else 118 | { 119 | httpd_resp_set_hdr(req, "Location", "/"); 120 | } 121 | return httpd_resp_send(req, NULL, 0); 122 | } 123 | else 124 | { 125 | ESP_LOGI(TAG, "Passes are not equal."); 126 | } 127 | } 128 | 129 | extern const char l_start[] asm("_binary_lock_html_start"); 130 | extern const char l_end[] asm("_binary_lock_html_end"); 131 | const size_t l_html_size = (l_end - l_start); 132 | 133 | char *display = NULL; 134 | 135 | char *lock_pass = NULL; 136 | get_config_param_str("lock_pass", &lock_pass); 137 | if (lock_pass != NULL && strlen(lock_pass) > 0) 138 | { 139 | display = "block"; 140 | } 141 | else 142 | { 143 | display = "none"; 144 | } 145 | 146 | char *lock_page = malloc(l_html_size + strlen(display) + 1); 147 | 148 | sprintf(lock_page, l_start, display); 149 | 150 | closeHeader(req); 151 | 152 | esp_err_t out = httpd_resp_send(req, lock_page, HTTPD_RESP_USE_STRLEN); 153 | free(lock_page); 154 | return out; 155 | } -------------------------------------------------------------------------------- /src/urihandler/otahandler.c: -------------------------------------------------------------------------------- 1 | 2 | #include "handler.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "timer.h" 8 | #include 9 | 10 | static const char *TAG = "OTA"; 11 | 12 | static const char *NOT_DETERMINED = "Not determined yet"; 13 | static const char *ERROR_RETRIEVING = "Error retrieving the data. HTTP-Code: %d"; 14 | static char latest_version[50] = ""; 15 | static char changelog[400] = ""; 16 | bool finished = false; 17 | bool otaRunning = false; 18 | 19 | char chip_type[30]; 20 | 21 | char otalog[400] = ""; 22 | char resultLog[110] = ""; 23 | char progressLabel[20] = ""; 24 | 25 | typedef struct 26 | { 27 | int http_code; 28 | } http_handler_data_t; 29 | 30 | static const char *DEFAULT_URL = "https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/releases-production/"; 31 | static const char *DEFAULT_URL_CANARY = "https://raw.githubusercontent.com/dchristl/esp32_nat_router_extended/releases-staging/"; 32 | 33 | void appendToLog(const char *message) 34 | { 35 | char tmp[500] = ""; 36 | 37 | sprintf(tmp, "%s", message); 38 | 39 | strcat(otalog, tmp); 40 | ESP_LOGI(TAG, "%s", message); 41 | } 42 | 43 | void setResultLog(const char *message, const char *cssClass) 44 | { 45 | 46 | sprintf(resultLog, "%s", cssClass, message); 47 | 48 | ESP_LOGI(TAG, "%s", message); 49 | } 50 | 51 | #define DOWNLOAD_TIMEOUT_MS 5000 52 | char *file_buffer = NULL; 53 | size_t file_size = 0; 54 | 55 | int64_t data_length = 0; 56 | int64_t content_length = 0; 57 | int threshold = 0; 58 | int progressInt = 0; 59 | 60 | // HTTP-Client-Event-Handler 61 | esp_err_t ota_event_event_handler(esp_http_client_event_t *evt) 62 | { 63 | char tmp[50] = ""; 64 | 65 | switch (evt->event_id) 66 | { 67 | case HTTP_EVENT_ON_DATA: 68 | 69 | data_length = data_length + ((int64_t)evt->data_len * 1000); 70 | if (content_length != 0) 71 | { 72 | int64_t progress = (data_length * 100) / content_length / 1000; 73 | progressInt = (int)progress; 74 | sprintf(progressLabel, "%d of %d kB", (int)(data_length / 1000 / 1000), (int)(content_length / 1000)); 75 | 76 | if (progressInt >= threshold) // do not flood log 77 | { 78 | threshold = threshold + 10; 79 | ESP_LOGI(TAG, "%s", progressLabel); 80 | } 81 | } 82 | 83 | break; 84 | case HTTP_EVENT_ERROR: 85 | return ESP_FAIL; 86 | case HTTP_EVENT_ON_HEADER: 87 | 88 | char *endptr; 89 | if (strcasecmp("Content-Length", evt->header_key) == 0) 90 | { 91 | content_length = strtol(evt->header_value, &endptr, 10); 92 | sprintf(tmp, "Download size is %lld kB", (content_length / 1000)); 93 | appendToLog(tmp); 94 | } 95 | break; 96 | default: 97 | break; 98 | } 99 | return ESP_OK; 100 | } 101 | 102 | esp_err_t version_event_handler(esp_http_client_event_t *evt) 103 | { 104 | 105 | http_handler_data_t *handler_data = (http_handler_data_t *)evt->user_data; 106 | 107 | switch (evt->event_id) 108 | { 109 | case HTTP_EVENT_ON_DATA: 110 | if (!esp_http_client_is_chunked_response(evt->client)) 111 | { 112 | size_t new_size = file_size + evt->data_len; 113 | file_buffer = realloc(file_buffer, new_size); 114 | memcpy(file_buffer + file_size, evt->data, evt->data_len); 115 | file_size = new_size; 116 | } 117 | break; 118 | case HTTP_EVENT_ON_FINISH: 119 | int http_status_code = esp_http_client_get_status_code(evt->client); 120 | handler_data->http_code = http_status_code; 121 | ESP_LOGI(TAG, "Download finished"); 122 | break; 123 | default: 124 | break; 125 | } 126 | return ESP_OK; 127 | } 128 | 129 | const char *get_default_url() 130 | { 131 | int32_t canary = 0; 132 | get_config_param_int("canary", &canary); 133 | if (canary == 1) 134 | { 135 | return DEFAULT_URL_CANARY; 136 | } 137 | 138 | return DEFAULT_URL; 139 | } 140 | 141 | void getOtaUrl(char *url, char *label) 142 | { 143 | char *customUrl = NULL; 144 | // Assuming the function get_config_param_str is defined elsewhere 145 | get_config_param_str("ota_url", &customUrl); 146 | if (customUrl != NULL && strlen(customUrl) > 0) 147 | { 148 | ESP_LOGI(TAG, "Custom Url found '%s'\n", customUrl); 149 | strcpy(label, "Custom build"); 150 | strcpy(url, customUrl); 151 | } 152 | else 153 | { 154 | const char *usedUrl = get_default_url(); 155 | if (strcmp(usedUrl, DEFAULT_URL_CANARY) == 0) 156 | { 157 | strcpy(label, "Canary build"); 158 | } 159 | else 160 | { 161 | strcpy(label, "Default build"); 162 | } 163 | 164 | strcpy(url, usedUrl); 165 | strcat(url, chip_type); 166 | strcat(url, "/"); 167 | strcat(url, "firmware.bin"); 168 | } 169 | } 170 | 171 | void ota_task(void *pvParameter) 172 | { 173 | data_length = 0; 174 | content_length = 0; 175 | threshold = 0; 176 | char url[200]; 177 | char label[20]; 178 | 179 | getOtaUrl(url, label); 180 | 181 | esp_http_client_config_t config = { 182 | .url = url, 183 | .event_handler = ota_event_event_handler, 184 | .skip_cert_common_name_check = true, 185 | .timeout_ms = DOWNLOAD_TIMEOUT_MS}; 186 | 187 | esp_https_ota_config_t ota_config = { 188 | .http_config = &config, 189 | }; 190 | finished = false; 191 | esp_err_t ret = esp_https_ota(&ota_config); 192 | if (ret == ESP_OK) 193 | { 194 | setResultLog("OTA update succesful. The device is restarting.", "text-success"); 195 | } 196 | else 197 | { 198 | setResultLog("Error occured. The device is restarting ", "text-danger"); 199 | } 200 | finished = true; 201 | vTaskDelete(NULL); 202 | } 203 | 204 | void start_ota_update() 205 | { 206 | xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL); 207 | } 208 | 209 | void appendToChangelog(const char *entry) 210 | { 211 | char tmp[500] = ""; 212 | sprintf(tmp, "
  • %s
  • ", entry); 213 | strcat(changelog, tmp); 214 | } 215 | 216 | void updateVersion() 217 | { 218 | const char *usedUrl = get_default_url(); 219 | char url[strlen(usedUrl) + 50]; 220 | strcpy(url, usedUrl); 221 | strcat(url, "version"); 222 | esp_http_client_config_t config = { 223 | .url = url, 224 | .event_handler = version_event_handler, 225 | .user_data = &(http_handler_data_t){.http_code = 200}, 226 | }; 227 | esp_http_client_handle_t client = esp_http_client_init(&config); 228 | esp_http_client_set_timeout_ms(client, DOWNLOAD_TIMEOUT_MS); 229 | esp_err_t err = esp_http_client_perform(client); 230 | changelog[0] = '\0'; 231 | http_handler_data_t *handler_data = (http_handler_data_t *)config.user_data; 232 | 233 | if (err == ESP_OK && handler_data->http_code == 200) 234 | { 235 | ESP_LOGI(TAG, "Version and changelog download succesful. File size is: %d Bytes", file_size); 236 | file_buffer[file_size - 1] = '\0'; // Terminate the string on the correct length 237 | char *rest = file_buffer; 238 | char *line; 239 | int lineNumber = 1; 240 | while ((line = strtok_r(rest, "\n", &rest)) != NULL) 241 | { 242 | ESP_LOGD(TAG, "Line %d: %s\n", lineNumber, line); 243 | switch (lineNumber) 244 | { 245 | case 1: 246 | strcpy(latest_version, line); 247 | break; 248 | 249 | default: 250 | appendToChangelog(line); 251 | break; 252 | } 253 | lineNumber++; 254 | } 255 | 256 | free(file_buffer); 257 | file_buffer = NULL; 258 | file_size = 0; 259 | } 260 | else 261 | { 262 | ESP_LOGE(TAG, "Error on download: %s -> %d\n", esp_err_to_name(err), handler_data->http_code); 263 | snprintf(latest_version, sizeof(latest_version), ERROR_RETRIEVING, handler_data->http_code); 264 | appendToChangelog(latest_version); 265 | } 266 | esp_http_client_cleanup(client); 267 | } 268 | esp_err_t otalog_get_handler(httpd_req_t *req) 269 | { 270 | 271 | if (isLocked()) 272 | { 273 | return redirectToLock(req); 274 | } 275 | if (!otaRunning) 276 | { 277 | httpd_resp_set_status(req, "302 Found"); 278 | httpd_resp_set_hdr(req, "Location", "/"); 279 | return httpd_resp_send(req, NULL, 0); 280 | } 281 | 282 | httpd_req_to_sockfd(req); 283 | 284 | extern const char otalog_start[] asm("_binary_otalog_html_start"); 285 | extern const char otalog_end[] asm("_binary_otalog_html_end"); 286 | const size_t otalog_html_size = (otalog_end - otalog_start); 287 | char *otaLogRedirect = "1; url=/otalog"; 288 | 289 | if (finished) 290 | { 291 | otaLogRedirect = "3; url=/apply"; 292 | restartByTimerinS(3); 293 | } 294 | char url[200]; 295 | char label[20]; 296 | 297 | getOtaUrl(url, label); 298 | 299 | char *otalog_page = malloc(otalog_html_size + strlen(otalog) + strlen(otaLogRedirect) + strlen(resultLog) + strlen(progressLabel) + 50 + strlen(label)); 300 | sprintf(otalog_page, otalog_start, otaLogRedirect, progressInt, progressLabel, label, otalog, resultLog); 301 | 302 | closeHeader(req); 303 | 304 | ESP_LOGI(TAG, "Requesting OTA-Log page"); 305 | 306 | esp_err_t ret = httpd_resp_send(req, otalog_page, HTTPD_RESP_USE_STRLEN); 307 | return ret; 308 | } 309 | 310 | esp_err_t otalog_post_handler(httpd_req_t *req) 311 | { 312 | if (isLocked()) 313 | { 314 | return redirectToLock(req); 315 | } 316 | resultLog[0] = '\0'; 317 | otalog[0] = '\0'; 318 | otaRunning = true; 319 | start_ota_update(); 320 | 321 | httpd_resp_set_status(req, "302 Found"); 322 | httpd_resp_set_hdr(req, "Location", "/otalog"); 323 | return httpd_resp_send(req, NULL, 0); 324 | } 325 | 326 | esp_err_t ota_download_get_handler(httpd_req_t *req) 327 | { 328 | if (isLocked()) 329 | { 330 | return redirectToLock(req); 331 | } 332 | 333 | httpd_req_to_sockfd(req); 334 | extern const char ota_start[] asm("_binary_ota_html_start"); 335 | extern const char ota_end[] asm("_binary_ota_html_end"); 336 | const size_t ota_html_size = (ota_end - ota_start); 337 | 338 | if (strlen(latest_version) == 0) 339 | { 340 | strcpy(latest_version, NOT_DETERMINED); 341 | changelog[0] = '\0'; 342 | appendToChangelog(NOT_DETERMINED); 343 | } 344 | 345 | determineChipType(chip_type); 346 | ESP_LOGD(TAG, "Chip Type: %s\n", chip_type); 347 | char customUrl[200]; 348 | char label[20]; 349 | getOtaUrl(customUrl, label); 350 | const char *project_version = get_project_version(); 351 | char *ota_page = malloc(ota_html_size + strlen(project_version) + strlen(customUrl) + strlen(latest_version) + strlen(chip_type) + strlen(label) + strlen(changelog)); 352 | sprintf(ota_page, ota_start, project_version, latest_version, changelog, customUrl, label, chip_type); 353 | 354 | closeHeader(req); 355 | 356 | ESP_LOGI(TAG, "Requesting OTA page with additional size of %d", strlen(ota_page)); 357 | 358 | esp_err_t ret = httpd_resp_send(req, ota_page, HTTPD_RESP_USE_STRLEN); 359 | free(ota_page); 360 | return ret; 361 | } 362 | 363 | esp_err_t ota_post_handler(httpd_req_t *req) 364 | { 365 | if (isLocked()) 366 | { 367 | return redirectToLock(req); 368 | } 369 | 370 | int ret, remaining = req->content_len; 371 | char buf[req->content_len]; 372 | 373 | while (remaining > 0) 374 | { 375 | /* Read the data for the request */ 376 | if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) 377 | { 378 | if (ret == HTTPD_SOCK_ERR_TIMEOUT) 379 | { 380 | continue; 381 | } 382 | ESP_LOGE(TAG, "Timeout occured"); 383 | return ESP_FAIL; 384 | } 385 | 386 | remaining -= ret; 387 | } 388 | buf[req->content_len] = '\0'; 389 | ESP_LOGI(TAG, "Getting with post: %s", buf); 390 | 391 | updateVersion(); 392 | 393 | httpd_resp_set_status(req, "302 Found"); 394 | httpd_resp_set_hdr(req, "Location", "/ota"); 395 | return httpd_resp_send(req, NULL, 0); 396 | } -------------------------------------------------------------------------------- /src/urihandler/portmaphandler.c: -------------------------------------------------------------------------------- 1 | #include "handler.h" 2 | #include "timer.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "nvs.h" 8 | #include "cmd_nvs.h" 9 | #include "router_globals.h" 10 | #include "esp_wifi.h" 11 | #include "esp_wifi_ap_get_sta_list.h" 12 | 13 | static const char *TAG = "PortMapHandler"; 14 | const char *PORTMAP_ROW_TEMPLATE = " %s %hu %s %hu