├── .github └── workflows │ ├── codeql-analysis.yml │ ├── dev-publish.yml │ └── stale.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── background.png ├── check.png ├── chromium-app-step-1.png ├── chromium-app-step-2.png ├── chromium-app-step-3.png ├── cross.png ├── icons │ ├── icon-128x128.png │ ├── icon-144x144.png │ ├── icon-152x152.png │ ├── icon-192x192.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── icon-72x72.png │ └── icon-96x96.png ├── logo.png ├── memmap.json ├── omgcable.png ├── show-me-how-serial-1.png ├── welcome-instructions-1.png ├── welcome-instructions-2.png ├── welcome-instructions-3.png └── welcome-instructions.png ├── css ├── bootstrap-icons.css ├── bootstrap-utilities.min.css ├── bootstrap-utilities.min.css.map ├── bootstrap.min.css ├── bootstrap.min.css.map ├── form-style.css └── style.css ├── extras └── stubgen.py ├── index.html ├── js ├── bootstrap.bundle.min.js ├── bootstrap.bundle.min.js.map ├── bootstrap.min.js ├── bootstrap.min.js.map ├── esptool.js ├── script.js └── utilities.js ├── manifest.json ├── stubs ├── esp32.json ├── esp32c3.json ├── esp32h2.json ├── esp32s2.json ├── esp32s3.json └── esp8266.json └── test.sh /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '24 0 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/dev-publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: publish-dev 3 | 4 | on: 5 | push: 6 | branches: 7 | - 'main' 8 | - 'dev' 9 | 10 | env: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | 13 | jobs: 14 | build-on-release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code repository 18 | run: sudo apt-get update && sudo apt-get -y install rsync 19 | - name: Checkout code repository 20 | uses: actions/checkout@v2 21 | - name: Set Variables 22 | id: prep 23 | run: | 24 | COMMIT="${GITHUB_SHA::8}" 25 | echo ::set-output name=commit::${COMMIT} 26 | echo "::set-output name=branch::${GITHUB_REF#refs/heads/}" 27 | echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ') 28 | - name: Push to Dev Repo 29 | run: | 30 | MAINNAME="main" 31 | TEMPREPO=$(mktemp -d) 32 | AUTHURL="https://${{ secrets.WORKER_TOKEN }}:x-oauth-basic@github.com" 33 | PUBREPO="O-MG/WebFlasher-Test.git" 34 | PUBBRANCH="main" 35 | TMPBRANCH="publish-$(date '+%Y-%m-%d')" 36 | WORKER_USER="omg-builds" 37 | CURRPATH="$(pwd)" 38 | ls 39 | pwd 40 | git config --global user.email nullworker@mg.lol 41 | git config --global user.name $WORKER_USER 42 | echo "Cloning repo '${PUBREPO}' branch '${PUBBRANCH}' into directory '${TEMPREPO}' via PAT for user ${WORKER_USER}" 43 | git clone --depth 1 "${AUTHURL}/${PUBREPO}" "${TEMPREPO}" 44 | ls -la "${TEMPREPO}" 45 | cd "${TEMPREPO}" 46 | git checkout --orphan "${TMPBRANCH}" 47 | rm -rfv ./* 48 | cp -vrf ${CURRPATH}/* "${TEMPREPO}/" 49 | git add -fA ./ 50 | git commit -m "automated publish of release '${{ steps.prep.outputs.commit }}' by ${{ github.actor }} for branch ${{ steps.prep.outputs.branch }}" 51 | git branch -D ${PUBBRANCH} 52 | git branch -m ${PUBBRANCH} 53 | git push -f origin ${PUBBRANCH} 54 | git gc --aggressive --prune=all 55 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues and pull requests 7 | 8 | on: 9 | schedule: 10 | - cron: '41 11 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | pull-requests: write 19 | 20 | steps: 21 | - uses: actions/stale@v3 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | stale-issue-message: 'Stale issue message' 25 | stale-pr-message: 'Stale pull request message' 26 | stale-issue-label: 'no-issue-activity' 27 | stale-pr-label: 'no-pr-activity' 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | .DS_Store 131 | *.pem 132 | 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SOFTWARE LICENSE AGREEMENT IMPORTANT - PLEASE READ CAREFULLY: THIS END-USER LICENSE AGREEMENT ("EULA" OR "AGREEMENT") IS A LEGAL AGREEMENT BETWEEN YOU (EITHER AN INDIVIDUAL OR A SINGLE ENTITY) ("YOU" OR "USER") AND MISCHIEF GADGETS LLC, OF 548 MARKET STREET #61961, SAN FRANCISCO, CALIFORNIA, 94104 ("OWNER"). BY USING ANY MISCHIEF GADGETS PRODUCT OR ANY PROPRIETARY SOFTWARE DEVELOPED BY THE OWNER ("SOFTWARE"), THE USER, EITHER ON BEHALF OF YOURSELF AS AN INDIVIDUAL OR ON BEHALF OF AN ENTITY AS ITS AUTHORIZED REPRESENTATIVE, AGREES TO ALL OF THE TERMS OF THIS AGREEMENT. BY INSTALLING, COPYING, OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS OF THIS AGREEMENT, DO NOT INSTALL OR USE THE PRODUCTS OR SOFTWARE. 2 | 3 | GRANT OF LICENSE The SOFTWARE is protected by copyright laws and laws protecting the confidentiality of trade secrets. The SOFTWARE is licensed, not sold. Any supplemental software or software code, provided to the USER as part of support, shall be considered part of the SOFTWARE and subject to the terms and conditions of this AGREEMENT. Subject to the terms of this AGREEMENT, OWNER hereby grants USER a non-transferable license to use the SOFTWARE for authorized network auditing and security analysis purposes only where permitted subject local and international laws where applicable. USER is solely responsible for compliance with all laws of their locality. 4 | 5 | LICENSE RESTRICTIONS The USER may not: (a) Reverse engineer, decompile, or disassemble any portions of the SOFTWARE, or allow others to do so, except and only to the extent that such activity is expressly permitted by applicable law, notwithstanding this limitation; (b) Distribute the SOFTWARE or any derivative works based upon the SOFTWARE, in whole or in part, to any third-party or entity without prior written authorization from the OWNER; (c) Resell, lease, rent, transfer, sub-license, or otherwise transfer rights to the SOFTWARE to any third-party or entity without prior written authorization from the OWNER; (d) Copy, clone, duplicate, or distribute copies of the SOFTWARE from one computer to another, or electronically transfer the SOFTWARE from one computer to another over any public or private network, without prior written authorization from the OWNER; (e) Use the SOFTWARE for any unlawful or unethical purpose or deploy the SOFTWARE to any computer system which the USER has no legal right to access; (f) Attempt in any way to obliterate or destroy the trade secret or copyright notice that is incorporated into and part of the SOFTWARE. The USER must reproduce fully the trade secret or copyright notice in all copies of the SOFTWARE. (g) USE THE SOFTWARE IN ANY APPLICATION WHERE THE SOFTWARE MAY RESULT IN DEATH, PERSONAL INJURY OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE. 6 | 7 | TITLE, OWNERSHIP, INTELLECTUAL PROPERTY YOU acknowledge that no title to the SOFTWARE or the intellectual property contained within it is transferred to YOU, the USER. The OWNER retains exclusive ownership of all rights, title and interest in and to the SOFTWARE, source code, and intellectual property. It is understood and agreed that the SOFTWARE, including any accompanying scripts and support files, is copyrighted by the OWNER and may not be reproduced and/or redistributed without the advanced written consent of the OWNER except where expressly permitted under this AGREEMENT. The SOFTWARE is protected by copyright laws, and the USER must treat the SOFTWARE like any other copyrighted material except the USER may install the SOFTWARE as provided by this AGREEMENT. Any rights not expressly granted are reserved by the OWNER. 8 | 9 | MAINTENANCE The OWNER shall not be obligated to provide maintenance and/or updates and/or fixes for the SOFTWARE; however, any such maintenance and/or updates and/or fixes provided by the OWNER shall be covered by this AGREEMENT. 10 | 11 | EXPORT CONTROL As required by U.S. law, the USER represents and warrants that it: (a) Is not located in a prohibited destination country under U.S. sanctions regulations; (b) Will not export, re-export, or transfer the SOFTWARE to any prohibited destination, entity, or individual without the necessary export license(s) or authorization(s) from the U.S. Government; (c) Will not use or transfer the SOFTWARE for use in any sensitive nuclear, chemical or biological weapons, or missile technology end-uses unless authorized by the U.S. Government by regulation or specific license; (d) Understands and agrees that if it is in the United States and exports or transfers the SOFTWARE to eligible end users, it will comply with U.S. export regulations and laws; and (e) Understands that countries other than the United States may restrict the import, use, or export of encryption products and that it shall be solely responsible for compliance with any such import, use, or export restrictions. 12 | 13 | TERMINATION The USER may terminate this AGREEMENT at any time by uninstalling the SOFTWARE and destroying all copies of the SOFTWARE in possession of the USER. This AGREEMENT shall terminate automatically if the USER fails to comply with the terms of this AGREEMENT. Upon termination, the USER must uninstall and destroy all copies of the SOFTWARE and all of its components. TERMINATION OF THIS AGREEMENT SHALL NOT RELIEVE THE USER OF ITS OBLIGATIONS REGARDING THE PROTECTION OF COPYRIGHTS AND TRADE SECRETS RELATING TO THE PRODUCT. 14 | 15 | DISCLAIMER OF WARRANTY THE OWNER EXPRESSLY DISCLAIMS ANY WARRANTY FOR THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE OWNER DISCLAIMS ALL WARRANTIES, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE OWNER DOES NOT WARRANT OR ASSUME RESPONSIBILITY FOR THE ACCURACY OR COMPLETENESS OF ANY INFORMATION, TEXT, GRAPHICS, LINKS, OR OTHER ITEMS CONTAINED WITHIN THE SOFTWARE. THE OWNER MAKES NO WARRANTIES RESPECTING ANY HARM THAT MAY BE CAUSED BY THE TRANSMISSION OF A COMPUTER VIRUS, WORM, OR OTHER SUCH COMPUTER PROGRAM. THE OWNER FURTHER EXPRESSLY DISCLAIMS ANY WARRANTY OR REPRESENTATION TO USER OR TO ANY THIRD PARTY. 16 | 17 | LIMITATION OF LIABILITY THE ENTIRE RISK ARISING OUT OF THE USE AND/OR PERFORMANCE OF THE PRODUCT AND/OR DOCUMENTATION REMAINS WITH THE USER TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, AND IN NO EVENT SHALL THE OWNER BE LIABLE FOR ANY CONSEQUENTIAL, INCIDENTAL, DIRECT, INDIRECT, SPECULATIVE, PUNITIVE, OR OTHER DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF THIS AGREEMENT OR THE USE OF OR INABILITY TO USE THE PRODUCT, EVEN IF THE OWNER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE OWNER SHALL HAVE NO LIABILITY WITH RESPECT OF THE CONTENT OF THE SOFTWARE OR ANY PART THEREOF, INCLUDING BUT NOT LIMITED TO ERRORS OR OMISSIONS CONTAINED THEREIN, LIBEL, INFRINGEMENTS OF RIGHTS OF PUBLICITY, PRIVACY, TRADEMARK RIGHTS, BUSINESS INTERRUPTION, PERSONAL INJURY, LOSS OF PRIVACY, MORAL RIGHTS OR THE DISCLOSURE OF CONFIDENTIAL INFORMATION. ANY LIABILITY OF THE OWNER SHALL BE EXCLUSIVELY LIMITED TO THE PRODUCT REPLACEMENT OR RETURN OF THE PURCHASE/LICENSING PRICE. NO OTHER ADVERTISING, DESCRIPTION OR REPRESENTATION, WHETHER OR NOT MADE BY THE OWNER OR THE OWNER'S DEALER, DISTRIBUTOR, AGENT OR EMPLOYEE, SHALL BE BINDING UPON THE OWNER OR SHALL CHANGE THE TERMS OF THIS WARRANTY. 18 | 19 | USER REMEDIES The OWNER'S entire liability and YOUR exclusive remedy shall be, at the OWNER's option, either (a) return of the price paid, or (b) replacement of the SOFTWARE. 20 | 21 | GOVERNING LAW This AGREEMENT shall be governed by and construed in accordance with the laws of the State of California. 22 | 23 | ENTIRE AGREEMENT This AGREEMENT constitutes the entire understanding between the OWNER and the USER. The USER agrees that this is the entire agreement between the USER and the OWNER, and supersedes any prior agreement, whether written or oral, and all other communications between the OWNER and the USER relating to the subject matter of this AGREEMENT and cannot be altered or modified, except in writing. 24 | 25 | RESERVATION OF RIGHTS All rights not expressly granted under this AGREEMENT are reserved entirely to the OWNER. 26 | 27 | HEADINGS AND CAPTIONS The captions of this AGREEMENT are for convenience and reference only, and in no way define or limit the intent, rights, or obligations of the parties hereunder. Additionally, any heading preceding the text of any of the paragraphs in this AGREEMENT are inserted solely for convenience of reference and shall not constitute a part of the AGREEMENT, nor shall they affect the meaning, construction or effect of any of the paragraphs of the AGREEMENT. 28 | 29 | BINDING EFFECT This AGREEMENT and the terms and conditions of this AGREEMENT shall be binding upon the parties to this AGREEMENT and their respective heirs, personal representatives and assigns. 30 | 31 | INTERPRETATION No provision of this AGREEMENT shall be interpreted for or against any party to this AGREEMENT by reason of the fact that the party or his/ her counsel or legal representative drafted all or any part of this AGREEMENT. 32 | 33 | ATTORNEY'S FEES In any action under this AGREEMENT, the prevailing party shall be entitled to reasonable attorney's fees set by the Court or by arbitration. 34 | 35 | SEVERABILITY Should any provision of this AGREEMENT be found, held or deemed to be unenforceable, voidable, or void as contrary to law or public policy under the state of California or other appropriate jurisdiction, the parties intend and agree that the remaining provisions shall nevertheless continue in full force and be binding upon the parties, their heirs, personal representatives, and assigns. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OMG Web Flasher 2 | 3 | WebFlasher is a WebSerial based tool designed to download user requested firmware from the official repository store and flash it onto the users O.MG Device. 4 | 5 | Currently only *Google Chrome* and *Microsoft Edge* are supported, as more browsers support WebSerial we can add support to those browsers. 6 | 7 | 8 | This release implements an entirely new control structure for flashing and handling of flash data from the device. Currently it is based on Bootstrap 5 with minimal external JS libraries (such as popper.js) 9 | 10 | 11 | 12 | - **Release (Stable): https://o-mg.github.io/WebFlasher/** 13 | -------------------------------------------------------------------------------- /assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/background.png -------------------------------------------------------------------------------- /assets/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/check.png -------------------------------------------------------------------------------- /assets/chromium-app-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/chromium-app-step-1.png -------------------------------------------------------------------------------- /assets/chromium-app-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/chromium-app-step-2.png -------------------------------------------------------------------------------- /assets/chromium-app-step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/chromium-app-step-3.png -------------------------------------------------------------------------------- /assets/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/cross.png -------------------------------------------------------------------------------- /assets/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-128x128.png -------------------------------------------------------------------------------- /assets/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-144x144.png -------------------------------------------------------------------------------- /assets/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-152x152.png -------------------------------------------------------------------------------- /assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /assets/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-384x384.png -------------------------------------------------------------------------------- /assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /assets/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-72x72.png -------------------------------------------------------------------------------- /assets/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/icons/icon-96x96.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/logo.png -------------------------------------------------------------------------------- /assets/memmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "1024": 3 | [ 4 | { 5 | "name": "esp_init_data_default_v08.bin", 6 | "offset": "0xfc000" 7 | }, 8 | { 9 | "name": "image.elf-0x00000.bin", 10 | "offset": "0x00000" 11 | }, 12 | { 13 | "name": "image.elf-0x10000.bin", 14 | "offset": "0x10000" 15 | }, 16 | { 17 | "name": "page.mpfs", 18 | "offset": "0x80000" 19 | }, 20 | { 21 | "name": "blank.bin", 22 | "offset": "0x7f000" 23 | }, 24 | { 25 | "name": "blank-settings.bin", 26 | "offset": "0x7c000" 27 | } 28 | ], 29 | "2048": 30 | [ 31 | { 32 | "name": "esp_init_data_default_v08.bin", 33 | "offset": "0x1fc000" 34 | }, 35 | { 36 | "name": "image.elf-0x00000.bin", 37 | "offset": "0x00000" 38 | }, 39 | { 40 | "name": "image.elf-0x10000.bin", 41 | "offset": "0x10000" 42 | }, 43 | { 44 | "name": "page.mpfs", 45 | "offset": "0x80000" 46 | }, 47 | { 48 | "name": "blank.bin", 49 | "offset": "0x7f000" 50 | }, 51 | { 52 | "name": "blank-settings.bin", 53 | "offset": "0x7c000" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /assets/omgcable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/omgcable.png -------------------------------------------------------------------------------- /assets/show-me-how-serial-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/show-me-how-serial-1.png -------------------------------------------------------------------------------- /assets/welcome-instructions-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/welcome-instructions-1.png -------------------------------------------------------------------------------- /assets/welcome-instructions-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/welcome-instructions-2.png -------------------------------------------------------------------------------- /assets/welcome-instructions-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/welcome-instructions-3.png -------------------------------------------------------------------------------- /assets/welcome-instructions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/O-MG/WebFlasher/ff20b73ab24f3e4ce37ffac8b8c127011d3a9386/assets/welcome-instructions.png -------------------------------------------------------------------------------- /css/form-style.css: -------------------------------------------------------------------------------- 1 | /* GENERAL */ 2 | body { 3 | background: #f7f9ff; 4 | font-family: 'Josefin Sans', sans-serif; 5 | font-size: 16px; 6 | font-size: 1rem; 7 | font-weight: 600; 8 | line-height: 1.4; 9 | color: #555; 10 | } 11 | h1, h2, h3, h4, h5, h6 { 12 | color: #00011c; 13 | } 14 | p { 15 | margin-bottom: 24px; 16 | line-height: 1.9; 17 | } 18 | label { 19 | font-size: 16px; 20 | font-size: 1rem; 21 | font-weight: 600; 22 | margin-bottom: 5px; 23 | color: #00011c; 24 | } 25 | 26 | #omg-info { 27 | min-height: 460px; 28 | height: 100%; 29 | text-align: center; 30 | padding: 90px 28px 28px 28px; 31 | box-sizing: border-box; 32 | position: relative; 33 | color: #000; 34 | background: #e11414; 35 | background: -webkit-linear-gradient(to bottom, rgba(225, 20, 20, 1), rgba(158, 0, 0, 1)); 36 | background: linear-gradient(to bottom, rgba(225, 20, 20, 1), rgba(158, 0, 0, 1)); 37 | } 38 | 39 | #omg-info img { 40 | width: 214px; 41 | margin-bottom: 1em; 42 | } 43 | 44 | 45 | /* this shouldn't be .. */ 46 | #qbox-container { 47 | background: url("../assets/background.png"); 48 | background-repeat: repeat; 49 | position: relative; 50 | } 51 | 52 | .accordion-button:not(.collapsed) { 53 | color: #000000; 54 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 55 | background: #e11414; 56 | background: -webkit-linear-gradient(to bottom, #DC3545, #cc0616); 57 | background: linear-gradient(to bottom, #DC3545, #cc0616); 58 | } 59 | 60 | .accordion-button:not(.collapsed)::after { 61 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); 62 | } 63 | 64 | 65 | .accordion-header strong { 66 | text-shadow: 0 0 1px #000; 67 | } 68 | 69 | .step-icon { 70 | font-size: 32px; 71 | font-size: 2rem; 72 | font-weight: 600; 73 | margin-bottom: 5px; 74 | } 75 | 76 | 77 | /* MEDIA QUERIES */ 78 | @media (min-width: 990px) and (max-width: 1199px) { 79 | #title-container { 80 | padding: 80px 28px 28px 28px; 81 | } 82 | #steps-container { 83 | width: 85%; 84 | } 85 | } 86 | @media (max-width: 991px) { 87 | #title-container { 88 | padding: 30px; 89 | min-height: inherit; 90 | } 91 | } 92 | @media (max-width: 767px) { 93 | #qbox-container { 94 | padding: 30px; 95 | } 96 | #steps-container { 97 | width: 100%; 98 | min-height: 400px; 99 | } 100 | #title-container { 101 | padding-top: 50px; 102 | } 103 | } 104 | @media (max-width: 560px) { 105 | #qbox-container { 106 | padding: 40px; 107 | } 108 | #title-container { 109 | padding-top: 45px; 110 | } 111 | } 112 | 113 | button.fancy { 114 | font-size: 17px; 115 | font-weight: bold; 116 | position: relative; 117 | overflow: hidden; 118 | z-index: 1; 119 | transition: color .3s; 120 | text-align: center; 121 | color: #fff; 122 | } 123 | 124 | button.fancy:after { 125 | position: absolute; 126 | top: 90%; 127 | left: 0; 128 | width: 100%; 129 | height: 100%; 130 | background-color: #000; 131 | opacity: 0.1; 132 | content: ""; 133 | z-index: -2; 134 | transition: transform .3s; 135 | } 136 | 137 | button.fancy:hover::after { 138 | transform: translateY(-80%); 139 | transition: transform .3s; 140 | } 141 | 142 | .progress { 143 | border-radius: 0px !important; 144 | } 145 | .back-link { 146 | font-weight: 700; 147 | color: #DC3545; 148 | text-decoration: none; 149 | font-size: 18px; 150 | } 151 | .back-link:hover { 152 | color: #82000a; 153 | } 154 | 155 | #log { 156 | max-width: 100%; 157 | text-align: left; 158 | font-family: pt-mono, monospace; 159 | font-style: normal; 160 | font-weight: 400; 161 | height: 256px; 162 | font-size: 16px; 163 | overflow-x: hidden; 164 | overflow-x: auto; 165 | transition : color 0.1s linear; 166 | padding: 0 20px; 167 | border: 20px solid #eee; 168 | -ms-overflow-style: none; 169 | scrollbar-width: none; 170 | background-color: #eee; 171 | color: #000; 172 | } -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* GENERAL */ 2 | body { 3 | background: #f7f9ff; 4 | font-family: 'Josefin Sans', sans-serif; 5 | font-size: 16px; 6 | font-size: 1rem; 7 | font-weight: 600; 8 | line-height: 1.4; 9 | color: #555; 10 | } 11 | h1, h2, h3, h4, h5, h6 { 12 | color: #00011c; 13 | } 14 | p { 15 | margin-bottom: 24px; 16 | line-height: 1.9; 17 | } 18 | label { 19 | font-size: 16px; 20 | font-size: 1rem; 21 | font-weight: 600; 22 | margin-bottom: 5px; 23 | color: #00011c; 24 | } 25 | 26 | #omg-info { 27 | min-height: 460px; 28 | height: 100%; 29 | text-align: center; 30 | padding: 90px 28px 28px 28px; 31 | box-sizing: border-box; 32 | position: relative; 33 | color: #000; 34 | background: #e11414; 35 | background: -webkit-linear-gradient(to bottom, rgba(225, 20, 20, 1), rgba(158, 0, 0, 1)); 36 | background: linear-gradient(to bottom, rgba(225, 20, 20, 1), rgba(158, 0, 0, 1)); 37 | } 38 | 39 | #omg-info img { 40 | width: 214px; 41 | margin-bottom: 1em; 42 | } 43 | 44 | 45 | /* this shouldn't be .. */ 46 | #qbox-container { 47 | background: url("../assets/background.png"); 48 | background-repeat: repeat; 49 | position: relative; 50 | } 51 | 52 | .accordion-button:not(.collapsed) { 53 | color: #000000; 54 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 55 | background: #e11414; 56 | background: -webkit-linear-gradient(to bottom, #DC3545, #cc0616); 57 | background: linear-gradient(to bottom, #DC3545, #cc0616); 58 | } 59 | 60 | .accordion-button:not(.collapsed)::after { 61 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); 62 | } 63 | 64 | 65 | .accordion-header strong { 66 | text-shadow: 0 0 1px #000; 67 | } 68 | 69 | .step-icon { 70 | font-size: 32px; 71 | font-size: 2rem; 72 | font-weight: 600; 73 | margin-bottom: 5px; 74 | } 75 | 76 | 77 | /* MEDIA QUERIES */ 78 | @media (min-width: 990px) and (max-width: 1199px) { 79 | #title-container { 80 | padding: 80px 28px 28px 28px; 81 | } 82 | #steps-container { 83 | width: 85%; 84 | } 85 | } 86 | @media (max-width: 991px) { 87 | #title-container { 88 | padding: 30px; 89 | min-height: inherit; 90 | } 91 | } 92 | @media (max-width: 767px) { 93 | #qbox-container { 94 | padding: 30px; 95 | } 96 | #steps-container { 97 | width: 100%; 98 | min-height: 400px; 99 | } 100 | #title-container { 101 | padding-top: 50px; 102 | } 103 | } 104 | @media (max-width: 560px) { 105 | #qbox-container { 106 | padding: 40px; 107 | } 108 | #title-container { 109 | padding-top: 45px; 110 | } 111 | } 112 | 113 | button.fancy { 114 | font-size: 17px; 115 | font-weight: bold; 116 | position: relative; 117 | overflow: hidden; 118 | z-index: 1; 119 | transition: color .3s; 120 | text-align: center; 121 | color: #fff; 122 | } 123 | 124 | button.fancy:after { 125 | position: absolute; 126 | top: 90%; 127 | left: 0; 128 | width: 100%; 129 | height: 100%; 130 | background-color: #000; 131 | opacity: 0.1; 132 | content: ""; 133 | z-index: -2; 134 | transition: transform .3s; 135 | } 136 | 137 | button.fancy:hover::after { 138 | transform: translateY(-80%); 139 | transition: transform .3s; 140 | } 141 | 142 | .progress { 143 | border-radius: 0px !important; 144 | } 145 | .back-link { 146 | font-weight: 700; 147 | color: #DC3545; 148 | text-decoration: none; 149 | font-size: 18px; 150 | } 151 | .back-link:hover { 152 | color: #82000a; 153 | } 154 | 155 | #log { 156 | max-width: 100%; 157 | text-align: left; 158 | font-family: pt-mono, monospace; 159 | font-style: normal; 160 | font-weight: 400; 161 | height: 256px; 162 | font-size: 16px; 163 | overflow-x: hidden; 164 | overflow-x: auto; 165 | transition : color 0.1s linear; 166 | padding: 0 20px; 167 | border: 20px solid #eee; 168 | -ms-overflow-style: none; 169 | scrollbar-width: none; 170 | background-color: #eee; 171 | color: #000; 172 | } 173 | 174 | 175 | .accordion-details { 176 | padding: 8px 8px; 177 | font-size: 80%; 178 | font-weight: normal; 179 | } 180 | 181 | .accordion-details img { 182 | width:50%; 183 | } 184 | 185 | .btn-xxl { 186 | padding: 10px 20px; 187 | font-size: 40px; 188 | border-radius: 10px; 189 | width:80%; 190 | } 191 | 192 | .agreement-modal { 193 | height:240px; 194 | margin: 0 auto; 195 | overflow-y: scroll !important; 196 | overflow-x: hidden; 197 | font-weight: lighter; 198 | /*text-align: center;*/ 199 | text-decoration: none; 200 | font-family: 'Helvetica', 'Arial', sans-serif; 201 | font-size: 11px; 202 | overflow-wrap: break-word; 203 | line-height: 10%; 204 | } 205 | .agreement-modal::-webkit-scrollbar{ 206 | background-color: white; 207 | border-radius: 10px; 208 | width: 20px; 209 | } 210 | .agreement-modal::-webkit-scrollbar-thumb{ 211 | background-color: grey; 212 | border-radius: 10px; 213 | border: 5px solid white; 214 | } 215 | .agreement-modal::-webkit-scrollbar-thumb:vertical{ 216 | height: 20px!important; 217 | width: 20px; 218 | } -------------------------------------------------------------------------------- /extras/stubgen.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import zlib 3 | import json 4 | 5 | stubs = { 6 | "esp8266": b""" 7 | eNq9Pftj1DbS/4rthCQbkiLZXq/Mo2w2yQItXCEcKddL28gvelxpwzZXcj34/vbP85Jl7yaB67U/LFl5ZWk0M5q3xH8265/OF//evB1oNUlNmmTjeCfYrOy5bZ8VmycXypxcGH1y0dT328aYP2n7Ue0nbj9J+5lw\ 8 | O+FPQe0iP7mo2t+0mp5c1I3X0FXbMNworGv80PZzfer2cY6Nc/ft5KJUruH3Ni0sleVG03gNfKEYvNB9e9n+Wg6etf9WDb8OC6kVNu64b6sGouUtdWjX1w5Va2y0S6pjfmxbTNUJNtr56xS/tf/W40unWPWtXVmd\ 9 | DZ597c2ew/IrwVLj4d1mbrK2Ufj4K1fiuC7dXPojofv0b5GD43sPoqL2YFWa+U15fOi3k0E7HbTHg/ak1z7vtRb9vnowt879dug3ej33/Ibtj2EGY5bD9en+ms2gjd/jQTsZtNNBOxu0zaBd9tt6AI/u9Q/8Rq/n\ 10 | 1G+cDtb1R370Ne34E3noOp66jseG7eya9uSatrmyfX5F66crWk52X9our2wvrto7134+dd9mn4Sj809Y9xDy5hopMIBcDyDRAyzq3nhrfuOm3+gNe8dv7PuN536jR5BfBpJmAKcdtMtBu05W7BL9J+7iP1oK/F4p\ 11 | 8XulyO+VMr9XCl3X/sSP5r2hY28HTnDnZbjjxrzTUpYcCe406F0z9pvVOm+JMr2VbrZW63l9cc5WqzWisNhaAIOwaeZ9CYCmERq3zX0GVRpG0cftm9RvT51Se7jHL+SpGwr+zWlKQAMo80YFAcwdWyJxOSZ7WEEH\ 12 | C7C1q88zQEXyrG2l8DoMncEXLU/aQQCBRn3xAsxaVLo/wDuzdiqk3FRRX13sA5DwlfvNXsC/tzP3IEIJEsmbYNAVNAm818qvPL4fkr2HINCXFqgaF3c77oPwTHqcbMJSaO0mW80m/OKIyMitv8Ew824PeY/T3iuJ\ 13 | JoO+BSJybCQd4iPhPN3hX6NAb0To9cR7bnwmUM7d+erh3iPiJFvyrzZ1ja0WBLL0H7cLFyu1k72nwnPL++NaGcXPTNnnQeeN+J9ukumyTUlOW+o12vk3vRHTVeAyyL2V99zAovdLb6eY0WB3Nf4ACTe08howkhst\ 14 | L3k/NPdhhEq2o3GPiWBDfdpp+tNOzT9lqSINJ5TUXQ/MQnnzF6nXqKBhsXHHe6HpSY3ShwyGqj0R4hsV2v8Re8rqrlOoAKH2BHOj+4yV+/TAhpXllELPKSHRNWzXeIlUnB7M8c/OY/xz8dDx1Bf8rUgf8bey/Iy/\ 15 | VQbdn+lDcpiVOJw1Lmn6eEPm5ndDggmgz0H0sWORs9ooVWTXItyhrE5tK6XK2LYCrootCJ/YglyLLeOtZklb+i5YEbPMKhLGVMkKI/OxDSDFX0YT6G1IKBeAZs0QwAZU5f52SHrLsoLAfjCYDt/z5Po3ntCiWNre\ 16 | ceKo/QIYikN6vwMGn2r/6RENXy2tSJOr3jQRYQyBoOGBSkmwHvTlI8If8HDJcDh+Hn/s87eyEVsRn9esBOiLli8FQyYOAZuJZbWCOjkygCZMrTnIDb2ms1/kHUZgxb8MBH3ePdVxtAc8FqFcR+d1HZ+MZ8/2Yxtt\ 17 | ILe1ckGXCQQZ4oBVU88/oLeTWCwSg8pRqyhoQL/qrV039xb0iGzU5yhdRtGzfWIQYhZhJLZ2QOZpGx224BT08+oZ46BF0wSlyoS4WSc8VMlGWp5549c1rLV9OGZgxsQxSs8puNVJCw9FwMUPaB8iItsXcgpmbKb7\ 18 | QFF4WoLBwFKsyRZBw71N9lYezgCRwJvU/xiet/OWGOQA1MnoBp2i5qgb2fLIQIwSbYUNiOT9mywf4+bqegLYuQ82/GhweU1/Lc61yTr+0+W6e3X+nteKGhGQ1B/2c/mZpDhExJbm5SkA9MJ5fA2RrJ3hS5kVVG03\ 19 | 8UvGUIGvz4iGlX4AnPkDzogmdOm9YvyeCnt+xSh0Jof8HPegvIVGEf+UTNyI7ReAeNx+sQj6zoy3WI0WXCKYfI29brKxKJIZ2M34PK7UoTyhHZDzd+O+u93A4MD207iJCtAp9JoBttHHzLeVgwlliarnvHfEkCzJ\ 20 | 9zbZ97wY3APIRknHZ7njhVP2QfQ/lsVfY2nAvIzoNdOz3oIdiOHpZvjiMVBjDZQSKcEGbYQd6lKWtwC6f4hrFaCBfkxQg9Vf65s0IAokhBMtcATyuxVzVSC9atX94uZqJp+TLeoNRTB/vTTOEcUgVsB8LB7J/BsA\ 21 | mjFpmM37tkrDTAzsVFWeegN6xZ3EMtnF6tcOGC+ZZ5DLi0pvSD/ocYYcuAu6U1OABWar444n3YwxcHDh6I+RHvMtyG8z/hXF4t3O1V05ncmQ9DFOF9KCLp9u4nY8mptb91gtlQFFUExMs5djCMfA5ga5DtQnW+AR\ 22 | DFUlN8GcWQNdnjL7Zt94erNJvHfqmecoCSAZBdHBF6WVv+QO5mhrN6boToPbuBRpgQoN5stBw8Ae0+YJvF6waml8uh91frcuDp62bu1fYYjJDTBKSY2Nj/AP62LYRMCJFC9D7fZ0P5jGAf086dgFdZ8CbRtswJiy\ 23 | JggtA9w57vL9QbAdjSWHk/gKnDhuQLkOqsV1MZx7cKx2l4Nf8czhDx7f7sS5iTvIidTAct0IE6d7wBsZN/ud0kCrezzHFaggXb1hEB4nw6kvCA4waiDhU7E8jN8TXyCJUBNsfR6LjaiWTLmHn21BXq1A8z1YB+M7\ 24 | DoHvkaq1s2+OyG7fV08PKCfTNzAzcsBAVgLOy5qs+GZyl7CJshOdFFr4nOQHSgdQWQhX429uCpmgnw+DZ4NkCUo3R+aHbHYB3seOrWGr1tMbsHPUd/Dv04jAWmKF+Mu3lJtE+VsxlilS1fXOJ+Zz9BrTPmkAeton\ 25 | 5CFU4w4cpb7ZRtvwyxcY2/jyQAzUp6QE270ypjl1hgm3R57hBbNnrxg3JulYph53ETNdehyTLTuPqyQUm9PIit+z34TNcvwdNA9/Rp37GB0w30BsyFzWJ5uwh8dizKHdAeRIxUwEKJGa6Ubc9Wlkd2b9PJku1yad\ 26 | jYKjAfbFLqhinPWQLafE7YW5CMG5jBeStENCmnvw6q7nfTDX9L2PWB52Xgc8xuBGsEYsU4Mwwoyyuhcc0p5TGsQ6jl/S+MJCpedCUFaXTZ8iG3ZgLsRFvPz24RNzL2KAy/HrLjFXC78rEqpJ1LkIKE2DJHCM7+T2\ 27 | 4SNed+YJRjf1kXQ7suuXuzoRcU07qt1mhkPb5BwV4TsY9C15HzB6EUe4QxmnRnivhXeTRs7jcBt+sKiSef0tPjZzj6Ut7CrqEYPfYsY5IzAOs7dvYPCjcL0It8++ZKvKHrx+QXajSY/sDZxhhz0Mp/1Aj6UtyAUy\ 28 | 6fwx/QbbEDagBVJqdQT/jrfeAKh2CwHZOLKju89BlH2A/bRLMgBCBK09uumZPOB3QTTyBCAk63iLJrcuF6BnvugScUCBC1BK8NckI9D/I+aeFgI2fbFcAKByxi+lvBahOC06OQODOX0PWvx0H8Z8g+GF4lvAzyJc\ 29 | 7zhKkgsmhqctZW6EbFKAKwBRvQr3pBENf5ETTVFtsUy2Mc+9pl4D/t6T2Y6wpKewV3oAzCmMppIWkqN2wi2Y8Gtyl6riqMvZAXpN/DfaoEbdEicItvw30czqI4i+En+A8oFaCzP+dyerdDIL9VGYEtccs8wgpyMI\ 30 | XgCsx+SNUBAvSIXXTTDmeKSvY8WW1bH9rNtLupnyZrL668xLVzQYb1ckSZDYsEeQQxPVBOBVzLsNpVZI7qolS8hyt97ZZwSyfkCfhx8UYKBAdQzSpRaGAEowNwjkp2tCD7RXEnqFYok8GMw693ygiq22ljtoPxUV\ 31 | l88UZOOrYjfqJ7p0ORXhpwl3JVq+dcCw1imA0Lx1mwjUz/oIuCAI99q/JRfO1Mke0aRE5++Yq2VIfa2fbN7dmne51zyWRUMA8ap1Pw+pdqWp8uIDyDz0CdNhuu1qjFjEiBWM6P8WI7wSipFig0OdU8YB8lDEXpgJ\ 32 | 2MlV5KyJagcTKWcuM9kwDf9x7MCv1+xKwboZBX3q1+pj1yprNGwdggI0WNwUCk09+gIvgDghfVhBLNCZ3IgJs7dqcUx52fWO+CQCU1kikLb8hbVhurREIGdItVkAlKNr1c5ZwdatYskSNTb6IhLABBGaRUSTfdWZ\ 33 | QBRrC0nBNNV7zuDFJIw0xBXwSxU06hRyLmlr7uWN2H5UFyeyAAHdwRVwnoXQGbD4aD6O0ncT6l/04oCftPvz/373C/3fEMDI3OpclK2jco8nEAiXCLy7z7hlve5QE/7TKWROp7Y0Bf1b/AxfD4VeL2i3ICNNjhkR\ 34 | hkVh6YUVEy/cGV+FoND2Zra+hryeIDslC6C8n8a5dP9VoKEhJAazV5BuLdVTkJrqFQceNA78itiZWs0Tgggs2Kc3glZ7FnYsChRtK9mObmMV4djpz9dA7tcvTn/GkBDweT6nlCd6lYiGDTYW0A1JVy4E02NJ+LMn\ 35 | e9EZmAxkL+apNpydH3QSJXoFEn+H4zAGJ5/Bs1ik4ZjWtgDbqVseZkx6y1uECa2N1gnW/IRDkrmEQ5SZ7UPQpICdWiQQJqO0izoTJoHwKhr41Ocd9SFWytMFAP0S6PVCwpcXc3/D/Na+lVt0L1Ci7BImdIFhWxCE\ 36 | OnwNr+rXXfzT9HJXMyeKInajmskcw6bhK3qnlVjdLgVdo6v5chGT9RSovVKBss2b/XUgRkM0fWnCIrzxapW1cC/u05rlBcyJrlYdeGEbEh8T1I4hzFbt/dXN4PUTK5pqrzoBNP2K6QjEQZNcS5jYMULSU7QPMaC4\ 37 | iwpoXUE+WkUlG59OxN/3RDxGAsMHyIes8ygz5RmoSv8w5D7ifeRzt8nm/bwu2+aOJuYamtQi2VEyLtymy1F6oE0vBcnFO/xp85N0N1pu6CKAUtTxCt5qR1fr2wehGqj2Fpc9TQ52HaEzJBhb9Rqhb3+fUpS6TCDk\ 38 | COoc2WbCCkOJUkTOf+xFCSiOqQ98yYYyQOn9lVsfI1KwgBLXe8pVPuBHxHtkULdwb+EKhabsImCsJ7Yoau8MB0eigRkDIQliCqwWunwoBVQfDgIhkLKhwcBz2EZWjIgVjSVWbL3T3ch+9oTZqJVkTqqxHE4YM6p5\ 39 | 2XGwJuc0joSxCdk6Dj+b3sKoVEzlO5S8HcFvI5ajlZdnBe8UKg9hkBxraw6+oDAPZniaL/a2RxyS4HlG5Dzn4OJbDvnVYyknKqGAx/BebrLtmyEbX+WMJHilpg/YB4XpzEsIJhiW2kW5vY50uYngnWMsdGbziKJ9\ 40 | +x9j6+wknTL25OJNnPAnIS9Yh2hDGHK4NPj5Vl1wyrrggH7uaUfTZcIZH5E1s9CcBRgdt7d4HzTTKDh7u3f8fRcmgNnMZHLn7IIxrd6hQnwHzbMzPQvVAt/H2MlbjjOxJaMNFwJBEYjVgK/0jCDPuXgF8lxaLyja\ 41 | 4FJPTizMwlvwdjR72aXF2tc3SZxgAjgLaFflAW0irH6ztJks788CSt5tTpBZHGMqcCyYYUAsl69imKj8QBEpCmrVwWiPDEqEsWTnJ3UxJCQNz67SbiuXLfj5e6IHPVMMWsqVO4Z+sGolMO8IGPWLi69hXCucsGFG\ 42 | gmAyCzl1UZjWg6WpAQmlCZC+wTsA5T0KgDA/+039togE/u13YMiAtCmhW44Ozdcw3Az4DrgpR4v7YGZvLsLPSJSjycTR0oJTbS1cuxtbXpoz58yy8dQkRuhdhgo6dl7UDdhhI45rZ5FouRscKxXc19g93fsAze01\ 43 | OKKRK/ZlyDeLMXwYaA4N4vRqBCIgizHVBkZIa9itd/l+HfftP5gM4UwOKIgCRGnK92Sse5r7FH4G76lF85ba3kB7ysGUi6wqfiFZgrjIbpZo770iuxdUzFJQ6yMsIS0lKNmv/fCqs4g8K6iOxIHUxb3r9a3QCtUf\ 44 | 4sKLj1e2S59QfI7j9KxwXcyObCEsrXLTV7Fki1ZAsRa4wEQsoYbwXz2626+I7qeI49ine9KjuyW65xrGNOl8UHHfauAfWVABvVPeproQ3yFF+kIJmcYS2zQ42VQo42102ifwE0JSDkzrCNG37JzdF4GrSemMk02I\ 45 | 8I/D2wDHAqORLe2/I6ikJGkxk+xd2tPxWw1FSSFeDZ7+hKx+5836QT3PB6IkBghV4uYMg3kZlPfW+jmhtgu7C5kiu8GWOw9cfyqrQmlVNv4IPl3t3S/Cjesc/HccxWpxs6H52FhOPnjZZfFYIzX0fo4mWtG49AWQ\ 46 | Czw/dBd9n9D0qLndhKxLjCPs9i0xrhrKxqhSElfwsftoZ5W3U+eCoFM89IUfUf+hI/yathzZVkuVzCVHNLFkvDVXIP3bohH+xK8rccVjrn1AN2LCZVuTvsbtscrzOVUMSmCg9QZh/BDYHEVYs8LeHQ8X0BqFYmdT\ 47 | MYTWBGF8+z6f0Oq/4DvGmkIaTbHF50GKaMTE0uT6NsUF2apVxokDKpfSBWgUJ8ik8sqiKVbvsaFSPE3l+RoqhSeSGROB52krk33GfM+db/BhOgy1FRhJBhnV00nFCp1E2QDWSUeeToKJ17tSseFJnhXqiVVS7lkk\ 48 | f6R6+phgru5FOquPCir9j9QTOsXFUD0t1Rua4u6lKuomD/sxummPA+NXkh7tbaiiUkx9quQKRlpKncggLH14kciok4qOsLpY61GXNAsbnTjUhE3kJkGBwaePc47XY3AXiQsNqMZz4rgf8A+ctupy9g2b08Sk0852\ 49 | 6vvwUixEnnSP6uE/EFVejLShwCi717p4MaeDH31zEVgqpwB4Z0dI7nktuIxdmJAdtcR17aiFroVatiCJZEojjarMK6YpiRq6WL/MTFC5ug1puwkzhrHJNGFbBQt/0FyI2ThNOAxQefrmElq8A9S+51IorLiYAGR2\ 50 | toSZAwR/Pbh8I/lWmMfOZ8vYIYae+djB4aeBnj4S7MQedjAlKq5ttiSFRq0UwpMHxSpL6g2nxdlubyqJnHu4FsRMPcQojv5igB2RGc+HB+Cg1Fnpm5Cql7LuBkAzaUOpkV7tCOILw2vbECbCeA2iBhhVv4646npD\ 51 | iqOL1kR5XbB/wGVDWAEwBsNEU5h98WlxNV1KqdnfO0HqCVEvtlbxJQErMyOtmbZxfX7EUqSzwL5+SDPz3/GDmaeDYGbWN5K6tHW9pEjZMkCfDyRCz+kryenbS1aLVmBe1Vzh8ilfvc566tWIei2HXt9qzw8SHcWf\ 52 | 4vZ9ClMgkNnNS7XrgDH+V9pV/bna1XLdQscCp30WuML7M33vr69dDZY5F3+264fWcyG7R28nBqNMdkNz/TNlgje211i4bKEY+YlT/hMstJh/SpWF5mQJ1r9Nurjc9QZZdZksyT9KluQT9sZY9PjixHavFZQi7STK\ 53 | NnkaP/UQyOWOiMPCbr3lg1aW/OCzf5H0aAHbkVCuRceMxQrpdnU7WtjRPRIGcrYKTaE9PlIEpoFFzOFBF8wSOyERklRo7M5z8JjL7A2mg6GYAwqWG/vhZIGudAnLzX68LtQrxX/WmUOErFGLkkW4c7qNDENJzsae\ 54 | EXLWPDoAdSwe3yEJb0es/VTx8zHzU+UXg/JOpPcL5gnWr/Ic6xyVU+fxnQgfxHdAq2ZcLKhFaLl+ikaBnKkaC4ya9O3rF37VikRj31OA2jivZeGd2ZKgYi1RP9gIqDomJJjcCaGJuI5YxRJ4acWPk6Y7bHXx0ZVu\ 55 | e1xdkTAQr/kfW33zHlbjKm9aPpsPyrUgn+bXLLRstP6ObjLCiD/XPaD3jlFzgIS+PMGzfjb18tXu7JZkLsEZb3341hkPqMpQu5ADMMPOZXU/n0CEPOmXkfyvk4dS+IFO03kfW67sgy8F+Oiw1g5WE8A+s3kxHwLP\ 56 | MS2sLIy7ysIW/r1TznqgJt2SDJ0UvY7oAZXymLkkef2xw29QEq3lRVfdZ1y8T3pheG+D9qsefwkHGcqFfx7nEvYxzD5myD6gOCH6asXmBT6iLxAnuhBmkvoHSlJyYGssR+4kqUixOdYPwGeQMKgxlBGOmCwxn1SO\ 57 | f4AvmF0E5nY56yIcc2E3EBJ7gfeAX8Zcvz0hLYCpmjHXYcs+xvOBNQaCLQ1SGaypPqSFyQlrLIFOn0gGjT/jD4z+8gIBwSz6jWF1mG+Gi1yloBZW3cZdiTg8AyRAfhL2GcJUeyIvod/kg669pVOvrk96xW/jK37L\ 58 | rvht0v8NYKu5bYroNqziQQ6ona5BXA1YuWCU5+q054jFvgaDV7vBtnOSpyp+AHnZRv8FUIAnKWatqbCCqegAlYKyCcAVnZ7YlVMtv1L+ULuk9PQdHwBr+W8PIt6W+EcOXUDsYrLLZa1YSyQl8tny5QmYyQaJqgzP\ 59 | bimmAVSt4o7JqvJYxDIzU83puJpr0DA9mMxXXNgigpbdBSWpR1TuWAduj8LtrbUCaqgrOksFX17wF+hY8bH2xo7WzMniLaGnfRFq6kv7/G8nizPeF+60dEn7p1F4gqWw60FwfixG9YxCO0bR+RYu/4y928LGQjgY\ 60 | b7ztjp4vEPfbBBPWwOYsWwwcU4Nxilh23S0t5wlgJegaMjkwe1qwZ9V4Z0HLyehb72yUUscgQ2sYG5VnPH/GVRBy1KqLpMvDHIbAcv3W7NjtCgJU9phFH25NPhOAMtwuH1zX9fJzkz3gh0YAwqjAocDkHdZA7A/e\ 61 | L/0LrsCtbaycsqHCjvdzPoBiLx+kd2onWXbjSpB/DQDYqJ0AnCfwikpGQsnV342dQxl6gZstkVMjZpx453u72fjQnEFHZIQR+w8/vKETJ1u7HYsrjrE0eR/m2q/6iGnPlLGERm6wK+dhvxkvD+ImyTpni8x62L/5\ 62 | w2/N7iM+fkOnhXLvOJjliwbQ1M9loxg+JOaO7jFceIJCQQwQlU1+r5tdl/iT69iF+QGeXpZVQoOlAFDRHolIBpRcuYjEjsP9/h0WZRziLRUh3lIR4i0V4X0S31r7988MLyzpqlBV74K8U/++mNOQs1q9a4VI9nkX\ 63 | QjA/xyfn8CznI5IoNRszuFihZHMDZS4W5ZYkIyrN2gdqPvsXLbm7iHp3IbRu9QJvOCr9O6zQFpRwFBfJ0sshx6IbvqenA55ZoDQhX1SpvR268jYfpx3kmgiHt9jHaLyEXpjbQ6p/IEyp9eN9BpTuctoTluzfPyFH\ 64 | ebvHj3qXVOD9HMfnSwhzBVVULqKipSWZpYtgpt0dPU4zpv6aPa5Aypf6DME/11uPhQ9I5KOZYQ67NWmJ3xjoyrly+LsAZxmzjOXWjoo6T6QFnZVSQi5zBRxE93/BkTnhNjzniIis5MqQul5GZB//x79w12qyzGtB\ 65 | sH48Qzt+R8vphSlVnPHhLJ11pRoiwSq4ZS9PzOMAqZM/Njvba6Md5MpzySliXQ2eugeLAg07qFys+gBHlcixvPc8LnoXV3krYd5XHuWQ7X0uX8Xdj+XuHfkR5TBszVIfMGviyUUDHm2+d3L+Fgj9rBPgGFdJWEZz\ 66 | cFGzPIb14ewlFZhANBspWe6tOqF0yAmCBljESpgm3dpBFqm47IePtwpT5TF7XBrjds297qa3jg3cDQsgQkwZi2GQjk5O8NWHd1lhN5AJKaFkSZePIORp3gIa8QLhZ+xUNt3NX8FKQdH4pdDPRoRHb/ecy4mydIZB\ 67 | FvV479ENJwCg73g0xpx5+kW0vRbs7I0ORUFFtVww8fcVCtFoUVzjYI206VVCjW58nA6Ac2eRufhby8UaVeEp9GZ4LhUJO19xRaC7HaSQQ7lyE1522ThckNtpgt4ar5PUS4KKRdO5lgzZbVCkPes86gwxcZzlohGR\ 68 | YSArcpFcYFSgHMYv8dXbDD4vfWV71r+pDWp2sUQQaVHitS7T1HuGbmcu9195JFjat7BJcc+6ndrdfSW7Nuo8iFKSlGJiDa8s0Z0zblwwRRwXTKo0fK9TRYdCFrAD9W63DRu1chs25Uw6e/Ao8SIGcFRyCxF/p8jx\ 69 | MR/grEI6w97gdSM8FNYkGxlzsuKiqFp3JytUInfFjAUTu5112RdUyKUlEUJuJWEr7JMERsen3eVL53y3QmH/C9b/0WerH/zGud+48Bvv+6xoBpcI5sO2f8mbKe+s0B/In0xD0h6Vb4U3zKnAnOdvmWN9Jm3J4Gwf\ 70 | lO47nPmwCRo/ex1PVT697YF32Yo6qmQ3v6GKutW8aCVUIWf1sP6ugSsns1d+UesjOkyz8E7eL10Ll4gBgvyzvdfBPP98cOME3emn+XacppsU32GbFC2Jje7GDoIGYqFxYIX37rHxahu4yRIPpdfobPMFbdasuO+t\ 71 | MnzfJ073kuYCv7iQ5Fs6e8O2XOVdnLEUzdgjAuDdNWLr2QO+UxP3khYg693uwiY8kdkcCkUQaF5IpXYZ8tyuoJbYRDVfptSu+6VkXxIBgFL5aK7BmoAzsfyxebojPEaZuu5mHUHnXBYiJ7bU7MmPIV/G0AzESrHq\ 72 | sia6FsXKK9HJAvclGGmo+SA64E6lc/oD7+zJ/iJlbNphEqBJo/0uQVTzvRyEV3e960QOTGz8ZRT+h3eHuhf0AP6/FTJlzCV3TfjrkjQ8lMLgQ5GgO11o03J4tR8PkXuP4NhA0+DlB7a/oyAwZb6+66WfxcrNPK/N\ 73 | yxJ+uniFiFHsLiBCo/4BZ0AAe6UXeFCZd623HE5RUKZVxzSYnBqcbER8WctSWkRuRiuAw8c4FR4ZiUa8sXoRGWcs4T1oY/QOEBrsjTdC9UW7DI/nfWNZSdF7rTPR3KvYe6gkimwNa7PgwpgmwzsFEjgCU4xHTw+8\ 74 | i+zS7hCdIGaCxzAVM5AuInfyRGkw6IuDYw6n1JNMYmmRD4Ch4hd8Msm8EEq8yoyjz8Gj4y6Myb3aRWwB/LEPP8ROLlsCBWEo8tgCO2M4m2Ous2v8DjJLWT6/DpCl66mxzCDD4c+DRP7nBcoULr0T+9jtfpa7pr//\ 75 | 5dwuzr3/OyU1/H+n+L8kk1ilxnz4f3giyVw=\ 76 | """, 77 | "esp32": b""" 78 | eNqVWm1z2zYS/iuyEtmRL+kAFEUCvutEdh3ZTtKp3TaKk1PvSoJkk7uMx3Z0Y8VN/vth3wiQUtO7D7JJEFzsLnaffQF/31vV69XewaDcW66V8T+1XDfp0+Vau+gGLtqbIl2u69LfVDAtPMkO4XLHXxf+1yzXTg1g\ 79 | BKgm/lljO8OP/J90MFgt19YvVSf+NvO/aVhNKXhrSm8Z7f9nHQqeFaDt2TGGuC9gTHmStQriqHLYAAt+NPdTgUYKdIBT3SFoaZqu/KiKpDYDFr0xsaiec3i/6jHlmfEcwEyjHi5O6SnOLP6Xmf3V4afVoN2JQW9P\ 80 | 8GeEoxrU5US8kkgqR9oIC7OkyFUZKdj2OLTJO7oII6jqxadNUTzFz340AWmGajCgrdkmjlIz4rcWZv08vy+2CKzUVaQ412fL9gTqcrV9TdJ0f0xpetsptmggID+ckA565o3cmCPg+QFZbQFaN0EQV5AlG5gF2gfF\ 81 | 4i5M/KC3woLvjRn65Vl/6Et+UFtgFf5MlFrVwXBwmQnrCN/UpPCmmTEJDfT9q5pVJ2p0QHfCY6zOAq6b/kba5RXtQKX/T607sGYUwLCN2cm39IpXBAvp8NERCnBg7DDsllXRLigz4yvT2ziTxvezmVyd0jC+Y9OW\ 82 | lDhGqWWLBowRoM0yZ0n8ptWyaRHS2Oi6BRfLopvYL8pkHGECb5LsbGemBYhhbLL8A0TS3hGdJcYdj7UvueSS3/Bc2jLGwOSs76LRAuhKReBGFhOt4nUOezvnyWmQvJ5GhsLmjevHRuAQxMroVaSHdjwnmFHqMxGA\ 83 | J9oTqPUcbSXa1p4BBr3a5aol0x2/6r61IqariFFEJTTLSDmsyC5InD8BT2ZY8n9K0E1zmZ5PHLk0bKma+Hd1+van8+XykEIJvV1zRES/PPYKy3gHMDY9ZJ+fkrOCVuvJJupBMNOw5xXhRFmR2KZ18m5ca+3RuIMh\ 84 | 3bp0/PMjoHIwHMO/RykQcMrGQGy6EQQd6JoicFM8PX2IioC5Q1JJESJLJchREXCaCJwDa9/CziAOJAQ2tWyFJuMskmD9AnwYkjRpqdaR9yXBBsWtegG0wKtBPJ68F6/cYWVFWIdcq20KfQCYuyUBgFAg4UAJogAZ\ 85 | I96SEBsk4SlMAPncoYTQJE4BcESPDXKYggUM9yfqb4ccI5LxpT2Ng8oThGRQaCEbPe1z+ZiYaEMrKIHnKt6whF0TptW0C/C8Klkl5RaVyBzH5j7p0sZ3haZhOvlX6FQ8J92csxm1SZIDSQ2T8AwtiO91OWSoQgGY\ 86 | myb9o1RKri/jGw9QFeL/DBDkG/YBQLF2GDJikNffZDs7xAPESM22IFE6lsnr7Cpe/yIktOje+vw7xx6fRrukw7StHt+4/fBWyc64wc8GZjwfziZgD4sJpa3oFLzrZfQ2QHRRdPOwDh9RPMA6oQzv4EbkZLVkLhmx\ 87 | F5SANuz+eMODuTgWxZV/Zi4f4s18F99cxzer+GYd34BSf2M8rFTrTLDeO3arnSJkyHG2rIvmjOTUCHRl0CT6c/p4efUGCB01PCXKKoJIF6EeQZklMS5eQcSavvZ7ZNjqM9mIitbF+dvMr93AG3/RsgtM3l8gS/Pd\ 88 | aCZu6ewj6V0zaEttRWZ2vRZ7zbv26vRXIxTw53momqgimf6IEfD+BoW4jbx7KvZ/DSnaIAQcjJ1oDKPgIsKOxjRv8CvE3wFxVW5o+X54f5wT7LqaE12kc77aLrfJQdIJ4V8dFwYOfLtI5h+YjIr1C08eBd1WG/Fm\ 89 | wYlZTclTIVkm7u8HIlOWNUUktM+cc1QwjOzZcvWW8tcieSFA+Irr20nw6aLhNXLKNJxpngELP+7CEqAJqLiT16QSyEHAuy3q+FdQ4IAUi3jAwtgNTKgDUODr2TZwqEPeg1EeDfyxlBul+7qPY0rsnv5wenhGfLY9\ 90 | CFA2pAyqnFEGhSTgRoUmBtYT6dNebdcrBHG3XLfe0GrWqVz7aStyRUjc3niF7UUU0qh7IomHsNCRRJmISBnqoc/veOmaMepSss/Zh32MVSbhkKU92tCVc3T1kv5BKjplMoAtlsRZU6xTZNs+tl222PeS4jxgH/k3\ 91 | lr9V3brw1bBAWOPcR2Ck7m8kvIXgoymc4uttWDiCmKpeDPOEbXxK79IiL8glnB54j69ydn16LjVHvSMXPn5UGPb2H/S7OmVb0u6wl9RsIPh7exawTFAqSHBIKNM0P5KBA+dxH8tb8fkwZKBjTv31l+2YLDVKR0Uu\ 92 | 2+iPJbRVZUGu5NIZ1MsQQF1CySHGUehQQFcOkYJdLq7g4+VBaSXLV2nuObloEMHIkHqgJih0DHdxZOpwT+57yOBd708gCqZ/kZS2FeuUsgKdrTrSXnKy4LgGCnqcHB2CzEfcG9Q4aQ8H9e3FcjW+2KWCH0OAy++I\ 93 | gq7ZxpBBeXtyyxfYpToGGtfHgwYvTkcdJtOzi3k3c9Hu4dHFkjsHVRIiGUR8MlLCSrBgh1v89XVvyHF8mLsGdDghlNZJNwFCapMQHWAcfKyIxnEObuA8CrLqtqM0dEKb0w85ySg+KHVnF81vklxQJoDGkt0NGp7s\ 94 | OM+g8RPQbIMol8x/Yx1lGPCaExz0yyDcNK8DXYdIOH/YWhT2lGKWYJUiJ5ZAmc1dGHdEjyoO4l96EBnFioDMjZCchyYPrFd21psLDHL6ZmWje1yp7JxY2m+YHLL0i0xfru5sABPIC2tOjbrqPo7YbY2K1u0uV7cW\ 95 | dxIP/95WFejj0uzByirpK3IWtBhNUzRF53+PBrUMvifmVNqNcu3ESV+i11FVDxPS/oQFdw9QH7di8HMybV+2nvWeOWEwDWaDqacaX0igwYwwGXALL5WmXgIhgm5rDp3hgrMhhqzYW1V5XEumuODStJK8uP3BNuUL\ 96 | CK5GbyuJMM2dsAFX/fRil1oFqFekxH1j3MSjEcoPfyfXgGWD0Ksr0wUZbVOPyDM8WhxThoI9toIoSfe5Tbuh9WD05UbWeQDS3XIoheKo4MTFo/peqCbaVLztEy6opdXU82AfBf8abJtebCy2T0W50X/lukbd8Q7r\ 97 | A07vaXkwU4jgLuox9EQ53SqK3RRlwXqCrc+uGQbJtGQbGPuMi42THThnqCt439CgAF4zF9FSmh+1tDLpOI1IhAIvkprMgVOetum5jUysewsPfPwY8QFEnQxEiuSELIJe+cLLmlcQtkfBviAkFEpa17dinevIni14\ 98 | Rfr49BlFeS1xveOPuFwhy6EA62lY8HpFWSIAeQ1B19acCODBWEXd4lqPqyNMCD49JLqAGGbyBTWS/TtqnRt6S/bEe+oq3sMMm6ZXYPnrN0B8dERxpYHE1nL/PI6fVt3DSYRkMAJVkO6aflupoHoIoALrFkNteRzH\ 99 | ExB1sk+dFazTICBNJcZzVx3maj2Di4E8yiIbo+jPpp22U3JODLCTo44HrTAMeoBWFk8fsBT9ibNSTIQjdWHqnm4qbEWIak0q42ZBTVc8FUowMq1/Bl5fxhkPzYkdROddgHbZKXuRgZ03/4H09hUs8AysZMpmaWRv\ 100 | 6y5wetJXrYHOj2K03T2WFZ5KrTTqtcXVJkEM5E1UrhWgj16MWbCD1zPwgENK4ez0++XVZ+poo2XUkWXgcZalQhO8D/YD0oSK2+BYw/VYsVyYQt/G6fWYe/wWCwBuXBfcuHDJzvJ2lwxB1VEvu06eYBvrM1wDfmqE\ 101 | uiLudxcW2ie6PCYDcBz0yzylSkG1xwzNcYAsa7b5u5o/6GxCwCdH6RbUQoyv1rxaj/hgELSIJUz9Ce6+BEyTTwmgssC6pQ6WBGmyKlsXpt6CSmacRmC78hYurmkZ3c8lXLZ4E87tsAqp5REp2m/HybfPZzSm09gG\ 102 | MHp6VuW8omlPmu4pfbXm/Ys7XPmKWYOYBQw4mFK4k5xudTamxBtPdkvqtmE5n92HQkHnDUUgnR9yXKjXDB34uHXOcZQsFIdEtqmvCXfMZNzyTcq4lu8eEmwQ1VTr4+biCYgjw6Q5Uzm3GDHkQtoDdlfatsqS+NRg\ 103 | OTU7QYcn45l/wgS7Col5ju62G0VDPCiq6Vqc0ZvB3h2XajjtI4wDZYRalD96rrIDLBY8MSnWBdOqBLn6QW5TvP0456qs7iWviEHgptgKuxwFRzbS6qp7xb4R/mFSxclEVBuKb89WkBkIpFNlp2YnmMAd8zEJKuqc\ 104 | W44MXDbZRAuMWRM+KG3Z3QQ37LRGMlaYaAAfeJHGOYgZjQjpMOUuXXizlPg1OYpLU+g5cvwqywFNw+47M4gHT6b+V2jiYOJc/0mhC3l1FXs5HhqpYz5chdQPcmFQquZGJdiBlUO0+PzHoo1jAfyznL7ttEdae3yU\ 105 | kol3I+juEdCCFwFQF9OXW85f6WUQ3aUi+pSSAAMO0d8MnT3vnbSBH9vXfKiZP+dGsiWhjMlnp2AtZ5xiWjGyZKvy5IuOazzdPIqYSbi9ICCNKcYgsnj9GijfvAGtX+I3RvbmHFPvT8V6V2Dljk8UsDtwQIxiKcje\ 106 | UWCYq78Px9NO35wTz4Uc1+ofWDEV8V7yqVQjFZahUz/Mq2XMSW3S0egvgGel2YW1bkj6SocoWrBxOjz935VzXx6ggFrA4bvhJjlko5i5lZEXFtxRL20bgdXqOlB27p5gBI82uWKWl7R8JiJnL/kWrU3fslUy3yTH\ 107 | J/k+AcJ/U60tgUxhPE+20echd62yMZ8PYJjNCACLKZ8R6ZR9CS0uP3s1j76NAROxbRXzgTQMXMOA5bYgNEcCBXN2OX8fcMFyryhMsGdv5o1MOB0tUmI1HP6kW7BMDwRAv9l8arTuD87/0a4AVp5xHyEs8vjPAHN3\ 108 | S26polZG9pq10Tk3bL/kk4XawuFR+FoL8xv8iCoJHzYY1qqkSgo/AJp8pq5pyB93abrKd4e0FaZX426cp6vxA1kXfXUohBtoWNGaw/Zcfi98mEWs4ewnfHi17cy+ks/0RLSs8+owsNLV1d7jAX4u+s+Pq+IWPhrV\ 109 | Kk+niVdi6p/UV6vbT+2gnurMD1bFqoi+LuWzjT1+EhOaZGo6TdMv/wXshTKs\ 110 | """, 111 | "esp32s2": b""" 112 | eNqVG2t31Mb1r6xl/ATaGUkrzVBabCBbE04SDIkDxOcEaSSF00N97MWJbQr57dV9aa606zT9sFgazdy5c9+P4T87l+315c6DWb1zem1c/zPwe3d6bYN6oQd+qcLd0+uufdTPicPFIfzZ6D9U/a87vQ5mBiMAMu2/\ 113 | dX40vNv/k8/6R5/3v36rNu1Hiv4317vBwjktdLb/W4yA9KgA+B6Cc4R9BWPmsgdn1HHqpAMs+tGynwowcoADyNoRQE/TbNOPDjh8Pzt49L05eEQn7LGFNc0EkR6BflcHT+bOyRF9xZnVn5k53hF+9/pd4S//UT8n\ 114 | iLRAmSAnqQmSCXTwuB8fCpGpFS39BDGfvqeHOIJUPblZPUEP8XM/msIhEgN8BC6sngJ+B4RvK8j283oW+Cqi0jaKXmGKlp8caIzV+j2JwNMxY3m1YfkFAPLDCflM+LwhqLjHgPAmSWcFJHfxFKEiiXUwC0gPVEUW\ 115 | ZP1gL20VvzuX9Hsz8VBt+kHrAU/4JzPmso3CgttkTCBcaYnaXXfAICzA75dappvQMADcjMeYlhU8d1Mu+tMzIn9j/0+SB5BgPIBjAfPZ32lJTwg+ZMBPj/EAD5xPIqu8USzARfDSPBKtUEzM9fvBgTwdEXhcAwaE\ 116 | oYli1Fa4NGNzAARtmMm+51srfFNGxavnwY54Pr3TelGne8oUMJ+EuaOZHqwJS5znHxgf2yti8HSKwGPDopC+5hU9lr7W5i59NlVRtQGqUhWxkc2EqvhcAnsXPDmPJ2/nSlZYwnF/LQcBbVetliI8FOUFmRljPhMA\ 117 | +GJ7AK1doLgotk5kMNLVn14OYMbjZ+NVl4R0oxBFq4SSqYjDhBwbiXd/BVNwINKCMtASoOg2EtjSOlHUBNhC88jCzkjwXXafVkztEMof6h/IdZ7ilv1RcnI6vTQDg2QYZcAqa6mtuzI1qR3bHwIAgrGpDlNODwMT\ 118 | 6Id0LdjVKnT1d4SRr4Hh2K4pqY/fF1PffQ/tC9i+lNeYVZjgjY1VGq/Mlp4n5O2MxB718OSCVQzdiGRDYIxocEStiiOFWvlmp1zjKHBgX+pGccHBXbVbGwMU4EdVKRKmDD3d5cFAEqU5229xNvbH605N1NnVG29F\ 119 | Htjia2IM6rGXXfciZ/HwFr0Ra46oOO8Piu4TNi6OVyBZBzdFvsxlZBwdGMcQSO1QeMAzVVUy6AWdt3NtlFhXROUeCV9D4075qVUyDPqaEgpBryzRFC4Zl/5LU7MbFZcq42FBxBoFC2oRg0JhRgB+AqDZioNjBhGG\ 120 | DySSTcfqJe+2ToZYiPfq8vWg4q5BLEKxunHlRBOqXJ7MYFpa1g48cy89Fdto4PCIfNldkBCJsCehuOg+ykP/qxuSqNvsDYzbNFJOdFwC9T9vV/rngjxJ0yxAWI0Buc22Y6AVGJmaiWVSH7Fr21WI3qnvThjhUX4Q\ 121 | wY52NDhhJelIn4zjpKntD+FCaeohbvA6f5EFMhJwRpP1rsnmb1+9OD09ZM0wbFBCI5HfU2KIHbKcOxxVzsm8gESE+WpQDYewORMmY26lY7M04oCEOy48SFjg8r3vdwHKg2QP/uzmACAYP4jqOSVtXYVRnMpTDiR4\ 122 | q1kEK34qqujaamSLsnsWA7cZYJLNGNVqiuo3KACBZAv8RC2e35ImiQ/ToTaab9trbchUpJfGeEeeJzka2jlw9JPIoxZr2kS5XWe2KnOHDOFKbAAC3E1VHcAY9kJeJBmPdQQT4FDhUNK0VGeXOGL3HMZKkE9XyX5m\ 123 | Hh6yhqR7r/3RYIbvi1u2lbB6vj6Nitn9a/3y4SUz2YUlM9gFGTPhFUUxGxvEDEgurLmNnyj5Z3rb4+hQyca8eBJYkPOxp5BpawW5C/txlRirFXxWVOHr5ADsiTnJKMfHaJh5UqvVYBzA1+vsdYSHcrFYSKnjGlDe\ 124 | 1hAryKZOYiAIPvtThHVc+aA58V6/nOuXS/1yrV84hELrtFFNSgK1qCIMmMcooo9ZEvnUztWRDMiO/A2c+zGaTFAyBHKsElgI1JnStvoBQvL5j6xF6CqdigIKnm/WuxRSs4vIFELp0/EbzliL7WkudPCRvKdlSyEe\ 125 | iITi/FqkqxxLVyj/0ExCPA5oWFV1mb9EM/zpgt3nUEDbkkyl51FdzngjsEhNERP+hncUbNggQsGtYINYr0TGn5JPT0vS+tAy7RDOi8v1x3YlHDQjlyNWi6LAKl18YBBZtMmS01ZDGKtKBhGRk36wogQJQusqKN9f\ 126 | sNTWNYOaQ7jB0lhyNg4GqvgKVOUtUAmWPpeA6QeSDJEooupnkgKYCn4vzDtY7V5uw6GBJhC+pD8SccCNglJWKFg9TWugKcpwxRkyG4gVpoOP7aKz8CGag8qMzQHOFXMwJ5aIeKByCANXPELz6Lujw2cQCQ5ZjWOX\ 127 | kB4cPRwcKTmUfkxlKe4deXuslOCXWLp1BwePJjWuSUGMLf3OkGXAttOio+P0xY3cw8GozLcKHuszJlcvNld1ZMkHBbN4djkarqlVueiUjDbu1hiVSlc6r2443X4tMTDwqc0Fy/ych435VRyYIxUlv2aeSezs5kMU\ 128 | /TeZOtAOhwnWhwsBGeTJ5Xv4dP2bzDVX/OTz3wYMiHbvB5gF512QxzbRupwlGI4qY6xD0yivG5SVYWQlkhiG4BLOlT5PShBzt8VlB9zgOSeKdrYlsRmJc40MOrwG+m/gv5Ygdt3+nSE0QsAbVNfqWk6XYXk1FCv2\ 129 | QYchKCjJSgSJ/VoOuoFZ0FkYPFG7mo378JCcacybmzU6CxaorpRp5p+vbncv0woIWop2DdwixtiEc386LiWG+paqCTsVyDumMPX34KbrLcn/pHOSvUhiWLo3RBEqkNI2uJcDjJrfshNp12xSYRT+kkwcDOjd7p2e\ 130 | rdkQYLlq9UC4h18jmFW6cpCUxBV4BWFSyCD0sUaqbVJ+g4QAfRZnoCABjhO+Kb1B8j3zoVegHd5TjdY2YaxTPq/RAlU1a3D3EtVAvb1r959CFDS/u+Ay3LT5AATMWc7ptKkAo+Cs+MgBra7L0czkSID8e420WK6r\ 131 | dEpK1wRKq+MXlAh1HddnVX+uZLhSpgHuWjPqipUswNX67fwKv4Fj6UyJ/WSV7gPGWiZkCAqxnNldxqSPv97A1+SQhYVqGzBpF8fgMOnx6eXN8TYXcyCeCuUVwQDvi0ax5GAb12dLfqCuFoA5T2YdtH/M7uZYbPeO\ 132 | x2G7DUlyfMqNhmoeLQ9Ey2RYOdqYkyRTiU52TvTO0uCZYfa58R77GLNYvgeht6kuDfUm23fjKhVUPSAC8W2MRkT/APFm8Lsj8kH0eMUk4zJWTYHaTnkMRllGWw4xumI563g2RJROwrriMdZRElbyQqIPCtCBqibt\ 133 | tccjC8DwCORA1eREGX2NEvCtqhklsATdMn6Aloox20DaL4QPdbG7uB4G6xCB1xo4fKR+0iK2VUhuaMriBe+8yxEgYf2zzESZ6vHiKkU3Bv5UaBATMlhix7PeD192Ruf+XarsePolVyPT0drDSJhOZhiU+h/l1eLr\ 134 | e645DfGYfM7G5DgFyZBveaTDW1UhLxOxNwvKm4y592zyDSIN4LV1itO4OSTB5Q3oU7iSFKzl5lUXczIQYIdCu0U8Eqn22W3atoxZHGYcGE2dAL3Bri6GkBcty8nuX0BsOBNxdk1xAdSyRdsKMXK2oMyCyLfNGVYe\ 135 | LRUqD5r2BJoz6RL+zc5hl1lsytX5CeXCHagsqet5Qt+wsVZx7uNioIx5KNSCnN1bycMeQNi85DQQqgJIEKxK4okLsiJDYXdoDp4Qd4Ayg1xU/OswwElWNtunUI3yFPEeV9z85UgC015C4spRHI7GNxsjwQeidsOa\ 136 | M/nVM52oaLL4TbngseQJb9hI+FTPus9uoCABGbiJLEFU32sx2Y6FC4Dl2JLU2Bdxm8sYn6PIdUM7eYdsIy5yQ40HKKRAogUtYm8Z83MQR2zv5QN0ORqX7muvlqeC0SvEiBEvt6IbpqKgYOURqy+xnUxYgX1DGaWt\ 137 | uaSFMc2taGCHcnGNNacsonB+TWhBkNBA1cOjcm28oxqbdNkAPch4nK8TMGx3bxg+9t+z+9jaLc7GkZa3bxUUrBpQt1lwK64oaiH6/As9+BuAuRkbXR3rgOeOvG66eLNPNBLStCuNrE/k0vtV3DK29RVjjmXoEoPQ\ 138 | VMKLufL8OesLRizmaqY+FdHo9IHItf5UxjDB2HMuUusDDC0yGwnjwWp5LPycSOAG16uagVKsGiidX04vue3sKWv33F9XiuWuSFlJGb4TCcLwdff14pvo4ZxfDeS0NwjYwcSu+UeITiEOAeEJ7knULQ75u2lQ/IW9\ 139 | 7dn5vujYk5HKHqFYDg21TdpzaBZJylKsSTkLCvh1GaSKUelgYa7YTqeLB6pekh+SR/JZj+JnspQ9lZeS93kiTGtFNUiTg1V5SLvauGqsuKLdPW71eLmaUbH0YrUNW7jbeNqlamJgnSPM7mJsgOkWxOqV7nPIrSUU\ 140 | ZBAKlALD1cPAFpy8cHckoZxyxIo2izu32s8NTq4cZ/vOvbrelHb7hmSXNzHPlsgBAnjJcl2hrmWUfLw6yplWct9E7YFjAcoNW8EZccGOLYd/w7kRVgPkk8cUCvLF8h/fypUf0BMtET58dT+WHmw33GzZ/3j9y/Pk\ 141 | hoNbQKmb/Q6LuGXhoD4DXQXQvwYko+HQtr7BTYtfVVkx7dhI1BcXgOMuHxG/Dao61HgxmbvgzJLexJ5DxlGVRJXK7M+EpvES1c/izc3lFj5vBYmpQKPcMuYSYoIMXxKy5WPw/nbe/VP4+AlD81+iNy1R1bbLONJw\ 142 | wcQUM1V4DAmyrBRAaPCv0tid7rEZTfg7h86NXOHR7qvBybZ8Ke85vX/kKLXVazBUxkCO7MjWERMAfAyaFHvEwmyxWrlSHPmWBr08xOaiFNiHNgSGh3M26Xzpb9TYhF89OyedcimX2NEf25/WFA9S8lR4Pcser8Gu\ 143 | IAgwvs4AV/ObmELWLhKmvi2VBWiYytb1jKZhjUzupUChwW29i3VKvJbT/nFKDsg/nyJ/QZlqF6R2hHRCXU+w777kUr+jBABsi5N8OxyObyKJWW7nd6UVz3YaZcVwQwGdH/pGETewxm2zdWdNS55WAilCKqTIyAU6\ 144 | +3CVF3Srx4gnz/2PiPl+BkaqdOUMSxJ7WJi5xXV5MY1G6JipKCKdxB6JICR5k7qlashWq7sdjkvBNV9makl6lxWZnxbvZRjMpwq3LZBK6tdQsWGN/2vR/wn9yNtZMiJ4QL6VVKkxudvg+c5HkPwvUK8gMLbN0K2f\ 145 | rfGrWCKtAdHKXehUj1UKbyHCV8luarlxjl10kCYxPiBl9VCi9oYKsaBDtR/dUlewQ/hE1qnhFlmjllm+/2LscDlC8lTfP4Bgz7+byClSDreDFgY4A7TswL1OWRHYuyl2uYGHClRQVxKd8OALy70f9CUVuc/2jEBj\ 146 | iajoRNiU0wlwHSAUYfrJEzz077ub7D883o0rY6sNWAax/dCrb1Yrj12zOm6Kn6Y2exfp+Bh0YqtQpbLBGnK7eAIJn6UpUHw/hXrbrS0vFnxIJHbjlXAUVLypnaooZShHDpc2YMVnKlLj15YzU6wtlNsJCYYr/sd1\ 147 | NrO3KfuiGiYCuIOgbSiB0t04tGMspYQazuY7rnqvYY9G/iOAHK0YLU0iKmNa7dyb4f89+fnjZbWE/4FiTZl5My+KvP/Snl0ub4bBcp67frCpLqvJf1Xpmkc7/GUEqEhTY/Iv/wUVadUW\ 148 | """, 149 | "esp32s3": b""" 150 | eNqVW2lz3MbR/ivLpXhKeguD3QVm5LwRaTkbyXYSSpYZWcWqaDAALLschqLWIalI/u1BXzONY1XOB0oA5urp6ePpnt7/HGya283Bo1l1cHFrlhe3Nru4zRZvun8y9eLD/YvbUD3uXlOf4pR7mYvbtur+2u49POs6\ 151 | ZjNqca7733YfChoI3+IISyNiZ2+pMzaW1Ojhe7bpPnJjk9H/WbbT9SgGU2Cv/KLbSgN9u4lM3XXJ0spZHA9/3c5MUC+pF7SkF9rpTtomr4jLdW2t730+hN3OukfX7dl1CzR59wV4sNJrnKYdwJ6b1RRDlmnvkRmN\ 152 | 3kQ1b3HTt03HMwvzLWEeINb0JnTUrc+Rl9j05qXeZ0czjKzHzN3Q2dvs3vlTasWe/vf0HJ/Eg1lk+2x0ALBDIQc5E2RXUdJYtuKqvEGnxBFlyQ3Ic/lbekhfkMPnd5Pi9LH7msNW5hmcKZzIpFRlJ0RvI8R2/brj\ 153 | cF4dca24FoZkucGG+lQN13T8P2mlJX4H/1gYr7Rtqd9PTuTpKazFY9wyziZcr6KSzlju/OriumvrvrtuY40ngUYeF2pjxUB3He/FapZX+ZGStQUPZ6b3ejoQWps2DX8g46Y74+B4D/wtDgr5Kx7RUekqrVX5s+Hp\ 154 | qwXwlHyiRhYTnuJzCYe15s7LtHPRYG/IAOF3T5tPlKFyVGooztdJg4E5QYKz7CNNAC3GgyVbwyAtblrsLzVf3cUmTtP/ftkftSGia0UoCnyAFsUcZuRQ/horRuiE2BJfqtRSEJNwTPiGlBY2pF1GsmYPun9KspRm\ 155 | 6yg7HHX18kmyBti1G18D6SuaB7QO+GHdKfwD4ut2HLWh0TCwMJsUW4NPeMArexha/OR/es6n3Q5X73Xsy0/q9Hrxen72FYlpU5CjrUqmemSTP8e0ZjEmAF1YwX+h58VGrPF91tCOXY9JXRfvngOTkF07xCToXQ+Z\ 156 | FQKTaLm1TxptGTafth3qbdu+uODjF0PKB94Mp52DJBvZkZuDtpO/oq3OiGK7eEgjBtzVc5e/c+7w++ZWf6x/lVj5Bgyz3WcVsWy/8MUt5TNYG/7c830sUD78HP3ZaaLeBjCnjBcW23bGMrpEOdn0v+CI1cQIPeNy\ 157 | 2L5WHVfJzyZHBzRlM/IEdsF/OO2MfWOurG4zXh/gTGb6uHGsXekU2kxgasVPh/uKmzvJ6+JMgo8INUYAWilMM22oklLZAapCr1yMTzBPgMB7JYD83eaH/DGQ+CXI0yT3CKONFwK+3mYQhZqnA1IaZhLO9DWdrFOu\ 158 | yopL9jkzxYApEEfRlBF4dTpiwa+5OftSyyOCEc3hk3Z06igBgAXAYoCXgSV8ewLcmEedIjfo7F7a1Rg1M1z0+Wy73AD3BGuZ4ljJqJ0CU9Ek5ERpENXI2TmDmDZMdNdWV3xALPHxe1j345wot2pgnNBSiGHMrE9b\ 159 | nK3e+zzBjySqyNNKMFjeTTXfEyniRdvltr17tXfc22q8sLeiXn5JTyekXtGA1Rnrm2cUA0LRY+XiIfhfdqNhcLpib1CEwKrVJITbLFoMm/hwxFaMo8WBLZvwog0rV10jXZf6BEEQF/us9Qt2Yijq93P/eRPmbNoC\ 160 | KRqiRwDSZG9a1mnsMAxoff6kHxwM/Y2d8j2Gl5XooG3TH3ZYxngrkCugmGCjjEW0gfNdjnmAPzn0AY5XTTJJlWdbaBltgF6Ij4dDBysA+mPzXQ7R+IwMo2/PVNvsCOzAbEbBBsH6lldkZavsLtsj3BE404qxXAm9\ 161 | HrAVAWmv7oMnYO+IcvWtWM8TbuFAp4exqi0YK1/TPiKjcTo90k57CtONVAKFsFtUAJv5W6PiBzMMRhHAWxna/A+RIx0yeha2tU7wS3dmMzqLgGs8gZfd48Oj3SRrHWy7VFrdiUeNxL1ani0YbjtgbvUK+r7+7uzi\ 162 | 4pR9N254T8DD2w4P+oJGZMu3935kw5d3ParFMGS+hdm6p4CKn/0dxKP7pyr+yQeasbFVB5CFUXRwtmEZQ9tw9hPs9EdaD3lWk0pXbKTQWHEQgnoPahIKiPyrfHbVdShnLzoOsHl0CI0ffb9Hvq9tz16dKsit7BvI\ 163 | fHDAbDEZjmE1SjeLA1ougIA1Qx94lpAYPSicrTE/AyFviBEdW9DWAaFgnEBERT/H8dOShGcEs5QZ3sYTFL7lBEyMWbrwaM5uZHn08hAO8NH8CP47XMJEIct56r4du6IUWesfM8ZWyaCTCKQv+bXzKVoewfYjUSrX\ 164 | Y1CtZ0DXgnFCO8KNf8FpWP+aQIbMsr1GkcnT0Ym5ooCRBNdblfXIk+7K8yAhhiAIdjeIwiuBWnXyUFOBps/uEUoaOgE86HYqwLEipsrIuhyAIe4rnEo2LNepPPxijizyHVKYfn68yP4ggp0fvXJPG8EmD9EsoYTJ\ 165 | 2a+m46KUUH2lXyDuoyzsyYwP9m38solPxc4OnQgYH5NtO1SW5miln+vk1CVq6JPwiBErhz8EMyKoLMnZZKUyS2EQiUQAbz6jDm04Hi4y3sIYNHw9P0E1Pt8l9OYV+K7UaFBpL57HTdChckzIxErG7JHfgcMjvDXa\ 166 | HR5lmDrHX/TZvdUvV/rlVr9setl0FZbs+BReGd/+oJIYq0p2egbaNnuBXcC0oUy/Ey/0Z06lIBh+rOJaTqPU2TRuJCV9l1QNLaT9QIqQbi4eott4z1GBgbi7vdr0RKhUSLVEM7IRYQLb7ksVy2isLrjYgL38jBy5\ 167 | 8FpFnDl6yU/05nQe0dB42QzsAcI0H8O4TjWuZf2GvEW3GWDgjMkxjTgyoj2IKeN9smGFK6GCDWs1AuYf5h++L+mgAieIyV9e7xNlhg0t4fSrXxl9lCVZEtDgJgwsF0LF9S+MlLJk553EmhK+Ij9H9vO8G+XJ9wHg\ 168 | 80WKtrLiF6KpArfparaqJSUbDHCp+HPS2GC1wMguPpBrjullU92sxT58JAV1JWGbkIHUZpbYUYoA3xDLXENH5lClwccDp2FxVHYmwo1AakMOTJyQWyWL4TNtMRpyOdhRAWtCCXuMySZuHIJ//Lenp88AQ1D09xWQ\ 169 | UkLeCn12eIOeHlX7JeS4oCnCYPsGiN+RfghnoINJ94wv5VvKYr6xJ2wzegHMINoZfVvivAfxpkkyqoMrJssZF9vzUifjCdPiE414gSJhP76YpTJ2bdYnXriXeINjKn2fgwRRLvRWZQorTiDW+9E5ClGN4KOziJOy\ 170 | lIsCB4DacXKPuIIncyhjl6qjSyFhq6JDG6f1VvW2S+7yS+zqJJA8VHcCdRpUcJoIZCGElFOaYxj5ZVJ3q/xe796XIgKliSHGGqB8+TfzMid4gijNKSiMqS3JuJBh3pMupxx11TvyYMQYHt+LqA1X2KEbsxYz4jmE\ 171 | tk3EQoaSB8XOmsM0QzqNSQpONcb4hc0/PPdRuUlKWqlnw1A+44x0yhS4BB6nXJ43X8ASL1SwbR6PQwFci+2/EIs2r9qSHcb2cjxRb/AoLIYth9Gd+eJsntDxUYImxiqnZV0KFPEKMh9YczR2pm/lVUIlqBiH6av8\ 172 | FH25iM2f+kQCLwGZNdPUbhIjXZ1uQoOfJfLk2rTKJhYmy/6CI1YzWpyy6hOLWw4XR+Ed7Kaa0CO3GB1BTjoJHAFRCwtAgiaTexC5GIGgynNGP2NoictMwBhQVMuH3WnUNa+pvlbquYmhesw0q4Rhj3yL7O0AfokJ\ 173 | keOvADGu7q85wZCN0PpFL2+Sy1SET4v3ZMS93DnVsef86eA6qh7q1m8Jgbl8Gm+Ov7/7j2AD26sogYyZ+cAzYrCc9Yo4OBK3YXohOzpqnS63ExGTLlth5uUUWymqONOSlSlm5tY7aJ2fspygGmAq5hC/wZr584vN\ 174 | 3fN9TnwDjAzlDc1hajbfJYcdOH5xzQ9UeAHTXM1n7ZewxuFuX2KPnvcDGBPm8+cXnEG0qloEnsn+M5xaJWsCWJ5WnuuVOe43mCOb7bzGaohZKgMAO27ycfbcLvv2CII8r75jH3XFUUd80ONg1wB8cpzUB1wckE8X\ 175 | B+ULiJsyuQJgm94W17NW9XYl62DxJeaa56zixQeBB4gkgLGY3fSINMD4yMw12sv1PCUznSYJjq5qmSRob69TQ41Ieh+4+4noSdVWrcyxJn8ls1dp9jV7kazvBUh2hICsqHn1Qwa7tPq1ytuWQBvneto+Q7m4IRQp\ 176 | g4IuVlHxMX4+GJyFXF/iCSDnrvnqJk/Dv0/MaaQ5Q+E/l1eDr228PJLvi956/4IjlqZlWuBS3UiWnIJpYbIaSXrwbNAGMgpyYaw6ZeQthLrlHahTuJHwdO/fLMmSC1z9iqDpgwjy3TYFuxagBoNu4OEcGEsm1BZf\ 177 | 6PT4+eFCEqMN2qmDCdvGJX0e1N4u1nxHifJlOYHJuXYFCG93DzEDCBGuFPFUy3MGJc0eRW0dbT+zc4hnqWw+GIkacr/h9IawmKGY/3LiEgDTYvVETmrB+l2DcQX21BhQr6WJPfiEC8WKFgxe1+liM4YfYo0LKeO6\ 178 | krql8x8ogxRUZYiTbBFuCLrB4UAQCLE/7Smo9LrX9zOCyAfbG9Z3OsmMNZg8VNfM+OGGR6jb/2zLrp3sutYX9wuc6QDc5uoYZCVd2CH+9ePzKraeF9at1MPChgUr4uiMeoJRjNGW4FKScqj8QMIPSOnqbP3vCX0N\ 179 | UYqHpgV7PST/iCecscxjRAAZluKt1iRuERNs+dQrvIW3u9cSXu1xBEPw9ICgHI6wMf8Hh67mA7rkVgcONHBQQ9d6yzi17ClXuVQZLmUx9jskh6ku2TsGVdqZC2EOCfuUavWIMLwKzOLqBNwwo9VspQTDhfUt2oVF\ 180 | ouLqlijDjIvRwGCH45Q65d/QXGDlWIW6fP+OV8ESx8VDrJ4rLvvC4iQWqROK8jnFCkJkcUPiTrz6GfHNDzDtbqqvaNmCOa571LjDZcfELOERJDZtL+f4gVB7N4qrnEx1w8TjHUeJdjcX8LVSuAhrIG8Yz2U3M9VU\ 181 | JPvcwbRb3VQmEJWZK77+0BuIJRcmMcaBgYeN2OJcwVowe3VkViqiyLJPFxvSOygHBfvpuJBRKZi9oaoUUoy/iTQhsj98tf5L8v3WjZGu9pcBa2fAcNr3YNUApYEgBbj+Fj1jzF8P7cMnxiGXV8eick8GsQqp8VNZ\ 182 | 7ETi6d1+qOUmLvIQJrX9/JZPyD0amxtOIuTrRyoRtjwlF+4WHZUfKTEfYsbYY42OIx8smgK6XRsVdzTj6gdUFvTdh0eE6OAcQrwSAxnGtLKFufZx39cqZEdsEmb3EThhNArxjJ8Nkx+BxRnkAgVBUsqB01uUEW2f\ 183 | Kiu7UAYnsWd9b6tF3cHYk7GCtd/d7koN2M5P8PGO1S0kbGVqyQiwvRThwo1VSci0kjvOt9ds87h0Du30jK+O+5bD/QAm8B+whHx36HQhhC7/+FepqgYN0YLgwp8epnySaWPx8PH72x+/md8x6Ad62tlvMIgvtSwk\ 184 | 2QCMoMdzXDkDvKjuEEQUv6rEcs6X26Z6dwY0HrJ1wLaopBLz44GW777lrBm+iVXH7F1Jh+ez45kwlKrUmxO+06BfSqyw/GXvK4GfkHuLwaVJ9ifjOmxT/vEGqFxhhh5P8A7DlR9FAn5D/1smFxtYEbNiprLIYS4u\ 185 | n2aB7EJ2Q4aXzrvX/P+MO/DeXg6wxk6mfC7vS3r/laS3YpTR4RSwDQuprvGrPd4uACEA7p4ANULrL6YSRP/H0Ewe0g21FCbGyyrEz1ybQyZ/cEEOfxVUSlR4WwX9K/bB5tFEopBrc7De3WTT1QneylaBK7vkm4J4\ 186 | /gofyPDu/StF1X6ZRlXbAvyq4gC/qmbUDXOdUga5kHrMb1Oqmcz653MVsJmj4Wbe8U1AkHwa8g2d5Bwraa4ZfHCEAckaK4mIcNoviCVb3DxOhSMNpZkOGA8V7PHQIYq4AScbrCxka6R/o+JpMHAj5MIN5gDGJn4K\ 187 | 737N2TSq+3B/R+KPF2Ciyi7uwnTNUamimwmvBbuko5rLkkeACoS3CwUq8gEUkUAzU7Wn0RHKz8Rq+ckNOLgc0xnXXvAGXrdj9FnYfZlF7uUQmbjk85oVR7Xk3KRYo+ZLY07WB454Ms5nNpJSlPg40BU0PlvOlUh5\ 188 | YjsOsRwGL3gB5O07LtnimwPUL7kfhAJmB72ARSFLzstbnyorvadhZG5BfwHngpeJcN2lBTpbGpc5J7EkTeSS1owclUwh4SoWmWABjkPmts0f5FIX/CiWCeaWgT+adBDrVhkUoL8uDvn+FvgDt7JQG4VON3rA8uh7\ 189 | XXEmtdTPuDgeUep3SsLE24QAwK04GzY5mu+v6Kl2z1+Qt4GsnSnTFWvFoQFGkWYi6jcct9ZDgX+aDBP9HTLbMAwxe/QSM4jRPJKDHc7XqB8c4I/m+nNvK/l1YtJjEMFXDPQDJ3KKIeQJoWQxURtLgWDER0rbp8Kb\ 190 | fe6+2p/zZWihkhy6GDqDQkNaDxYPc5mwzQd5YSqwRjPmUuEWdn+oqu2WWyqva/kZn+yt6E0xTzT1mXXwYIY/kf3H+42/hh/KmqwsF6XLyrxraS4313fy0Zqlg4+133j+Ra264T3gFj1RlpeuKOyn/wJ487ar\ 191 | """, 192 | "esp32c3": b""" 193 | eNqVWmt7E8cV/iuObezAE9oZaa8mMRKRkGUwpRTimoqW3dldF5KowYhg2vq/d95z2VnJlkw/2JJmZuecOZf3XGb/s7+oLxf7B1vl/nB2ae327NJFe/5fb3ZpEv/p/8o09T+it5ifzC6rzA+XaSRjZTryv7OHs9lg\ 194 | dpnb2WVW8mftnyp7g20/nfdGfneDSb9vZvz3zK/O+rPLgvb2f37Q9j/NLpvY/2h4Ze4nqxg0+Glr5Tv+zNtjEPbfsE3un6BRMF88A+cvcBY5Ro2DOXmayHnWm+osPWbmL515CgYWfjQiBi78CTxPTW9QjnY8gWyX\ 195 | eSh7U89Ub/Rouu2fNXERDpXJofZn87UHuvKjnkDteba5/9L4GeeZL5vUPwe+/vvEr6tYjni2adJ0zYSSHrJoVGFVldJJeT8wn4jMeq3woAK/HXTZx+ePV8JLdkpqA/PjQMf4zyxfUuIq0fzhlZBmCoMOD0op/Lbu\ 196 | RyiAd/f0rm9NpkjGlXv95H3e12vU6yh3T5SyGGvhH3bNm9SyOG2f9czLbXbkiVTeeDM78lZQJWyeJIZohDloj6mUEavImBPQjR+C2VFXcJb3z0GfxvyWqT9GnfACMrJEDN3/5Tnbko7zJiqL56wCkMRnme61IntE\ 197 | ShlOxKHwWDzBUhynYP+wjs0cRLJa9sk2uJ7td47iIMuh/2+9UdUxj2bivRmI5djffgMPgCUZtgUsa/LTQyzqeHU8ceyHKt0sf4wRL95KRprqlBUPaUDeNpUtbeCtNPA/Z2WzPpsa+GtqbDpRp+kYhn8E50jE44zY\ 198 | nA1L9MSNDdShj4qoEWNEqseo07jrW4CxX5lwmY7JNsUPsCKLxzA3Nsw/P3RTORmB6KRjyTiVUxH1Ow4qcFZhNX50EOQRlgx5vRXOSXYGMwlmejtQuGydJTCQHSZS6PG7m+f2PLBD5KNABLL0sm4UNrAnIaXqSPkv\ 199 | sItao1k5jJo8nXy9DOi3AImJQLp1J4EvPpJYYKU/cFgm9Q3PeJtkJRqxAyhxFV5ky6q7ZS0MV2SZK9iWC5vwK+AVOYc4N3EJqMxiDRADrzMbQ1IxfzFmE3K6OOUBb2gX4y67ViTKdM9F3n1AwVdx6EGkqiFyiZ8u\ 200 | 7u5eiQfdiL5h62W8gG6KABU0GavDVvZhGHI0FDavczaHZR8nhyuDbzubhi3EMeNWJdfTk3ueY494peB9GQseY5tc8or1WQj0ptkHeWoEzK+rDqexnvxLNw+hdOLG4ET5B75ONPWgFONZR5gSl4BECCNCoajlKDnJ\ 201 | zjN3dyQzyDHyPcZ42z9j8CPxuyP3ym9j9OfI/QNOAJ+FRT6gkHw02BP3qtlM11tLs5K0qNXwCQeEnzN6EOlewZY3I4ZnI1YDzpCngxGHJcqfJGGS5GkpZyrjBzWboSZ4S/mazR9Ixubqh790bW7ErG9yzaqWGAYf\ 202 | rMn2J3wUQrV065CUcvGgm5ZMFLQGN1BDWMgaCbqU347Xs0BIVUIoqQROzSclbHYEMdTkVhGSAj2b+A4HdkWoDnYumD0AmevGPIqo51fsBRCjExDBDKwjqw7Y8GBa+Cwth/0Mi8sdnmCvH8ITcg6DLIznHEZXcePT\ 203 | B1i6vfqINe+gwHhPSNesjAqRVfKvqpDzpVcLxpQ8xNkFE880xxGvgWk1xSF7NnvFNotSnedmXWxxQFne3MTvAwUy3GIyZPfjTOu8kznQHxjNBROYlTUs5sLi8uMdajWkvn2LM1onK5yFlcUUXwBYsQsCK4vBPWBb\ 204 | MZrNvW81yVnzGgJ+PW1tC86fu49YpEj37pgBAYrI4me3MAI8u4YKdbW0x8bDsPmTMRaMYgwUu8N3XaQFa8kroIH9cPXxJ791H2exf0dq7x3JVZKvNduAtT241zHi2DP4wAu4JyqY8j18aN76gQorYfO2K0qhXD+T\ 205 | GNpUnzWZORTfU09oMrG54g9cg5U9+bQPVnLMXvCYtlxFxDKHXyElpITxuUNo/MzQlcUVahiwqSaYaR2bdiqBJS/BorYoPt5EdwtKvfq3klhPgM7oDhhCyIbd9cWeiwu2dFolj2Z2eijVa7yadKaBMi8n25gItPTv\ 206 | A3tBqUl85m1KTeAR5PqvILRCks7WCr1Ghn2JeQUFwo0CGKBep1hWyCeTe8fZVynMljkKpaYnrMuMSR8MJdREFNE/SxpII0tM/HSLFsp8yc9OOBv9vwS28x1rAsbqa10g/QmljAsdbzabIJlhcSVJRH8c8be6EMlW\ 207 | h24gWVGPs7e6x4qlyoy8bdWM0q4ZySDTf8ElypwLFGI6ZkY1W2wTeli3JvTUh8IgaqE6kp4N0Y4C7WXgLpOOEXcA/KuAWx/+OuBumivy/jEtnOZvoFebr4IBg0S9WqD2Dm/b3q3EgKZ4/v4lIurL4zOI+ezea9jy\ 208 | 69n8b5h88v4pJp8en2Dy5G43+0pPh9OzD0FXSIwgCR9oDiVfFIiHCVeRVE49RvLK8Bonsd1JbKe5glmnAiRhM0FCiHoJ6Tssypk5L1jnl0SVk6LsYfp5pXpUkSmik5MWQM6St7C9OzlENP4nrIKSy1jU0Jazy6Xo\ 209 | N+eHZJJbkiNqCZtmg7ZVyE/A2EpaV2jX51j4KyADWlWwLITJOfcFHQMEjNCatknm3o6RemaczmXxeDzeIJe6FsZi9ByNfawNCK1YNj/dhJaue5IGf9ZavKYWgZFEt7yFkXVYgiqmqUWB3NJCeaKtHPVokjxseaVm\ 210 | BoKNb0EqqprP0yciefSVbLpcs3JAbYaNSix08Sidygkc/SEXQMuf06eq5g2VBTXFcPJSoyIbf9vPkVQH9oMjoq6i8SiM+8qEGxFzaUMQPlsp3fosF25EzhZYSlWMzBf95fX4q5K71H7T5mZyQj/b5sb9Qwhs/C1E\ 211 | kifSWUijwLVLd+V4FhmWSd9wtdP6Gx6CReBItQx0OwC/Rsvl/6vOb8qp6y/f301QSDfaS9QGkOXvleUa/kbbrplnG99SdNUm3BHMZpOVQr6nJi6mYqh+lSKnbFQUUWiYtXIppN2EzgmVgRqHUHXVEsFyh3zc/XgI\ 212 | jZwJ2uSqQFUYOs6BmCo6pm4pUCO95+bIQSA0sEYTJTEGf6SJbHYR1qP16X6RmWi2oJlYmbfTEOmoW9AW+nLb8b3+RthvYmqlfC+RNwvPLbP7AxkYnf2Rew/IjEgoTnvIL8A6/Wz458WKwAAftGd8kx1agTSOSCL/\ 213 | WNKiSkpSVH+I2XANSDW3j4U+NfiQosaPhWrMg4BsB92ZeOxY8YT2tWivyo52dhXZd7Y5WJGZVlwDFLHqo0q3EXCcCJ5yoccilQqt/MIq8UaIJ912Sk/ieKP+1eMQaqsR4dOHHn28op70AWqgg+DFcplU2Z5qEno2\ 214 | yPsKMeaCLccRpwhNq6IgyUFFUHMjFwqVewodIW/AXUNujoBvSCez0Wbfyww6cjlQnFse1KC7Dzc4CTYMvlHNsbEOaSK/ZsUIji2/0rBtCPXFioxkLKSorolLp38h3sfaEvmbaKpGmHeNcMJBQCCTkuy6g+a5hG5q\ 215 | Kqa7O2wupgxN+yIi4d/hk/PlwhZdSqgxWmwAO21tpd/eCF2IJ1diCDpl2zZpXvMsjLEgldFlhhpOPL6nnjI9EVegPWRvEuAjHRy0u4pY6mxFCEmoNLxTDEM3Ws7pmFrdTcKC6P2ZJ+EQBDlwEii9FldjaH3Z9fuR\ 216 | xuZpN/7wXCVG17s2s5BO/Ao6U4FgWNh1p/dqhcNCAMPEo7G0YsXAqE3WMeza/NaP1KCn0q6EJCqROCU71P88Z2RSieSU+tgfsN/9g/vIUIZojeXVleBOdaUFSTWQiiCTzKSgPrR05tbxZswcBlXN2TRrCZ5l//Md\ 217 | Bk0qmtLhu0AGkSUje776Eka/APduIZaZnyWM5cdMr23Or39s8F5DHylgdyidkUZAmO4h4haRRxJ1xLZpvxZ6Cgv4iztPhwei8EDujpxEZHYhv8WRbGFxaZRVmmPo04LBlM/y01WIv6Rc3UJ63JkUck1MF0qENnxd\ 218 | FUlfSK5Sy1Rut5o2p3gs/mUlZU+lA1vL5UOlUI5UpxD55BKxhOFKGC7kXG2yfLPimh/0vvSzgIYJKAgGakrW3kw6oTcK5k3sNNUbPPM781Q1R6NNdvkXupD4K6w6GlzBJ+VyWK9RciNw5BTHG06QskYzz0wcPp4t\ 219 | huy9emvF0hAXzvp3juWajS5+uMJAWt8sRYa38Py70tLBixoEuYTrdRJyWi+jroCkAVgRZn2rz0hqSjhXqoYpyRSfarKB40ZG3dnN6KVzhsSi4hRjV9qMRpy+XLEYa56FHDlXFKS0RuP8snGIQCnp7u8Glq1cLrVd\ 220 | D8JCT38uqQ59Inq3lyhwtnJJimgYuhM22aWwy7u9k+ZcdyanGUBnPp1ItKcM3FE2S2F5rGFZXx4qNuXiSwZqr+fmVDJUkiPQGyzTG6K5976L8JKG67sR7gqsO+XXRrSS/I2kxEqq845pdPtD/aAe486YI35csgDp\ 221 | Spi2CR2CsKAV3j2hU0bX02FExOgN53LrA9VJeB+KDzmkXOsjFQHbIni0IhbcwA4B3RCiW8ELFtSCAv1P4YZRBcjhwz0nE52jo4A1f4QIP3F+ThvHunGWhAaFauzmQ7ydkt8u3zmK6Ju8y8VUAm8h7zlA3ssrVAbc\ 222 | C98fUr19dxN1BPmJ1BIe0etO7Rh/6IhW1WNGmzA3X3pdDT7/ZPUtICliSDyngmiWTPkT23Epb3ypXUs3el+QBKCPIbqZlIi0RrAH4TCZOVy3bCv5wHZ4U+bPav6T/1dKCet0kLGWuLX6gszvepmpVpOwV29owMmL\ 223 | TM0hdxRbe6N4irfn/IZz7jzyTYPPKPVOJAoUTHwk1+sNZ2MXR4/QhzyQJqSeoyy2J70jeZNH+/RoCFgTslft7Fj96wvUmnK99h20n/VO6fT7knHAKtA/LwWcGsr5KjkwdZ57p4gGlAVwKaVdZ7mcNZ3XDVyoSgnq\ 224 | 5LKe3sjRyEW9mkS6/nzuf6HElAZMrs5YcZJB0aOBSCkgilgJ1+pAWcLGslfSrUwhJbSiGj3ZMZKi4Qpj1bPWO9HdTTnG/V+ZJiVoFF5LCThFoJk1nZcCN1s//e2LG0Vjxsgyeb73EiUoahW0dJpk2uDiMTl+DIdO\ 225 | nuyhXE6eUtMNV0Rv6k6TnRzd7H+3Re/a/uPjorjAG7fWpGlkbRYZP1PPFxdf2sF+lGV+sCoWBb2aC2EPyF32Zbi7i7FJlJvo6n8hJolJ\ 226 | """, 227 | "esp32h2": b""" 228 | eNrFWml32zYW/Suu7dhtZjkAxQ1p40iuZFl2kmZ6knqSI3dCgqQnXTwTR0mczui/D+5bSMq2lHybD7IlAAQe3ru4bwH/s7+orxf7D7bK/fm1sfNrGz5lFr7jY16fzK99Hr4N5teFm1/n1LoXGoun4U/6Y/gTh6Y0\ 229 | /K+3wx8vT8f09Py6qV5mNMej8Mc8DvMPFqEV3c38an5dm/ArGpbjnbBAvssylNFsfl1F48PZdnjWJEVYOAqfMDbPh+HPYL4/v8QKmO99mCGh+WiUy5ahNSxQB5mtC1+a0OOD8GWTzfdJrv+ehnFVGF/ys02TZWs6\ 230 | dOkRq4Z2Gj5VldFOeT4In4rOolZ54RNU58LHD/D/+6XIkp9hj0MIP+nWMeF/7kasgrsXdY+WsjSvMOzJoCt1v63/Hgbg2cN6t6eGxeJsDPUE+7gBzxssGmzk/KmuDHuHh4rwsG/OM8vqtAO2Mw+3+XFYpJqGue04\ 231 | oKAKT9aqhniMPliPVyljNpExT7Bu8gjCjvuKszy/w/rUFqbMwjbqlAcQyFLGBH47x1jSdp5EdfGMTYAl8b/M9lqVHZJRRkFuZ3kKk0wxFNspACfIxjDHInkt8+Sk0+2wMxeNV1QrqtGteOhyFP7aAKo64dbcsnpy\ 232 | LOYwv/0KJwBIMowFDGvc2QEGdVMH6TyfQ9Vu7o7QEtRbSUtTnbHhoQ3o22Yype1kKw3On7cy2YChBvmaGpNO9dD0gBEewT5SOXFGMGe7Ibrjxnarwx4VrUaC0VJhNxWW8rengGC/88JlNiFsyjnAiDyZAG4MzL89\ 233 | 8jPZWYrR0x6SsSuvKhr0DqjQWYXR+NFjkEMMGfF4K5KT7gx6UvREOzC4TJ2nAMgOL1Lo9vuTO3vRiUPLx90i0GXQdaO0gTmJKdVGKn+BWRSN5sZmFPK08/U6oN9CJCbG0u1xEvriLQkCK/2BzfJSX3FPwCQb0QgO\ 234 | YMSb9CJTVv0paxG4EmSyuyGvcScHkZvB16l6GPIkT3kPffoB4MAWAsoCAA5dpSPMBo19M5YeuBK3x0fZDl4yxsmP+GP/Ikxj9OfY/wN7hWngRx4S8x4P90SLNR9LJ9oFHYBm6UwbJpLa33BP2LBPdJNDOilzenY+\ 235 | ZH2F6eYk8xyMOeBtuGw4ZgIiTymuUdxk3zteJTWfG3XkPb/8/ltxy75+9Ju6Tjk74iPX7YX2UwpX2YRJooymvBFCb7Z1wFap7bd9BzRVeA7vXhIckDfCsDj70WSdHFvhC/goODimSI0chCB7ihhpGKNngSidKWOH\ 236 | KVyx2DslCxYPkPV9diPuvFgyEyDAgQvx0gOA5NUDxh7Qhf+lZYLPMbjc4Q5mxxHciWPCY2U8Y8K80z93rn/VowBYRpxJ1GmVKX2dGdFWDbrRnkZ361rhFn7uokdm0U2vQRSed95i45K2W5K9ALRbZl0AIuEFb/W+\ 237 | HF053WUizh/SuVic/zq/axBPGRnFOJvAuRHhvH8L+rDLd+h6g5WTPTFm2EJV80YriV2qQiTKlgBGwfGB0NOClZFrfCDCQvCmOGBBmWq2GZzKSOugvX9zapP80s1PRFBMR8xoHKNc9HwufSCmEz2zIGsEdCLg6uO9\ 238 | 1WDQZHsTv73elW5vJ/iCI4ZvJvGdrspieB9WKsbzy0BUTfqyeQXdvpq1BxVk6vw7DFKbvTlhgoUN8uTpZpYtWnzdYNm6Wplm+/PTwBvC2zinxLs7esPsxeiFdOkL5teWxiTaufPoELLs27fLdz+BBl6Bi34GoEoh\ 239 | GPKP2yDBPZzME+wFzq1BVlVBnsEvzBS1ALQQm6qKV+SI/sp5SwnVlinTD+jBR5/ZOjgsufBo/ciT5kmleMo1ndsM+M14AV0s/1g/IWHRP2A+JQD624PDqlf8CLGuhkN2diBJW3Iz1spunCGKJKzEg9UYEyF0iEO4\ 240 | aUr/ls8XkQj0UiSZOnoANRnhZCXZU0ZW8SXI8uLzMItDyuyc//CGwVIqpTokB00kcrc9fxqx0XOScfBRcmNqWZHhp8+HIn71kDzhIOxLFQYP5sVp53BtMzi9MEmBMU57ms/LUUZLCaqglCTmH3XBT9buwP/QIRpe\ 241 | po4eSha+XlZqJCT8yOH4JQfjJGsCRtVspA1dkcFp6GrtlBuhUYQYVJ34/xLscL54kXQc+/7btw9vnvbNiqYceJWNn91/DhA+n1++hPCzX0A4xcnJKTpP7z9G5+P55RNQ9fmTXs2lzM5Gs5dvO/Uj5MNOA+cfyAkR\ 242 | qi3gQWOJ/SNm1MrwGC8e1ouHpb6CxQfOKXyMONBFXgLaAjK8ueQBm6KMwmu8lz8Cia2kQBo4aRCDtrLwPAM+NroHU9jJPzkqzFnVCaOmTctWU6qvLg4IblsSA2sqluRDLaHhW18IxEzGHmkmq+ZsK2Q2mdB04g3X\ 243 | BsKN7yp08/n0NjRKmlJ1QFvMKGacs/SboNMUG2jclKJnLp8gR6K9FGxP0fJlW52TkgLkw3kjuQFJS40Jf+GCyGSzWAUVSS6yU9kY6hs2W42MERTnSTNq1BRdNYnikyDqAr4Yf/Pq1+yxGnbddgvxAK5UH8X4bYsK\ 244 | mew75/3Ba1N73LWHtImz4UvJhYktLRsKg6EUrobNFxhKKZb0F4PV8fhU6TdUAxIAm/gJ/dSCG9JIKKWwk69ZJaVVxs3iTnaf7QrMbLMNHZ5zLtYCFhSCxKwpdaEvSzW2mP5W84y1Kna9lISYq94crbhovhjAVWda\ 245 | K9MCh+XvRPaJ7jHu8ph2w4WUNCon2ad6ANQ6sBYdAY/g1X9/AIW/4bjPO7UP22PGzatGTI7EOD6771ECT9kKtq3TBakADnQEgFx141Fb879KTzxfHHklHKqMzzpHQ3WKRp06ldNnqf503FcmB2+ciGtuSvmQMEOW\ 246 | PfS/g8JiUoRnaq+zHyEx/YRhsufzK1WSMEo7Z3IXqKwwHPsJ0XkicUclEUny8DspVBL9HsniVDVCAJgcyZJSSoJdPIxlkonnqgFRby3mqvLjnV3lz51t9h+UMUp2XrBxaoq8cX4aUTbcaXUkKqlQHy6sLi7VsCbv\ 247 | V24irF/J+qkeTLSNlWw2gBiqokE++c5ZsKSeVr68uEqXYmMY3dixJCYNrmEUL5Q8FNVRZxYSE9YjO8LbNFK+rjz8e/4Y7vkYezoGkY1Rkh9v9qx5hZTcgau57CIZuqWk+onqjJHlKwXviDpci+rfpAPGaEUWWzdU\ 248 | 4hR4GQkwyIh9yLel5YovEzzbshG8xDPFpzh1JE35YMp872VDGZdcW+52Ul6g0kK2u8NggpfTOnERkwXuCftQ3XOL6uAo8VTkVj0TdoukQXsJQeMKETHtumxbR3GeewHVguxmfA9WyeS+HqLZUg4KzSFz09OH2jhs\ 249 | Z5XrgDq/oYS0i0TCkRl1uavssz3/vZCpp/zcTrtNEEBR5YTZazmIzLTP+5QwVjc86/sZ7qsEedGtnoVkATfImiL1mpVdx52EVu9aLMPDJOOJlIUFYlSv60Ls5SBWSAuPkxoqUTdlb1SFvWDGUnU4Cn8s5SX3or8A\ 250 | kCOUk1y15Fy6qZaaJlRDbqpzOe8FnRqpD94t2Os/AKXqkkFZSxBaDj7eYyalvCUbvenW8JeShpnlp671E/hw40pwuB/FQbgTXq/R4tymxwrzQd0gaX93xIROPhIIgbHKpCXrsWRdAmyaUsjndwQ5Se/RbnTcjXb+\ 251 | WKId9abeHwt5VUnOBuKQT58u+Om6bp9WbqrFsjqFlNkp5kEA4HJOeBd6NxJLYUXu7cpMksWmDS6O5GRZCeszKQLXUnyslMmTCV+ks8ObSgsJLM6EkmHsq42I7zbc1UO9nPsodCEpMxwFBODDcT7t+eO4wzaJ01Tn\ 252 | eOYDy1Q1x+NNK/5M1yJ/B6Tj4RKnUcITvcxxRojIK4c3HCnljcaWuRz1kJaP+NxWeslopThDF8WDeydULpfCL6cRiN2bFa/wWupvWrTDiwHEt0TqdT8kHnzs60gK8hUR1tf6jGRnRHKlGpmSCDlWTT70XCSoe7MZ\ 253 | veTMEXNUHH3sStnfyKEvb4DGmqddzOqUAiniUU+/ig/RKcXIg91OZOu6OhPVIIgIw/qXEgXRf/jvtiSP81auKHILz3ySAljf6/JsL+/ocUafsbOpOHvKNL0vBZHxZNphss74DnN9XL6CUXs7TqfMpJIQgd6YmN3h\ 254 | ysMBvOpeCvADP95CZOvP+DUFzRj/TVpiI9WuB41Gzm/aVXCcKqFor0IkBNDUyGt754HFi2Ie2mV8O0yGO4zP2Yuv8VKvupdveIcjirPeUUKwLVofEHMt+MWKzpkb4nQrt3ispwU5+Z+6m07VHzsQ/4wQeimhsfkX\ 255 | 3R1y2E4TJzpxnnZlDDXYOu7w5gUd3tW7T1F+4/qCzMT1FnKzDo2vjlBFcO1xf0TJ9TebBTDmubjSkFXVvXpJ8ranXzWQWUuDrycrb0fhyJ/efOlEchtSz5lwmiUkv2cYl/KCkcK65IR7X4gEtI8muhvNulLfHeA4\ 256 | 6naSm4NN2z9Exuzvjv7Z0o/Ae58YQtzCWSTmyq2+j/FBb1QVNSkf6g11Mnlvpjng8l+LN/KoeFkrTHjJZUKu8IdoUu8e4m4FkxzLNX/DwdjV8SGKhg+kYigi53Rvsj2NjuXdEYZQQOmcAwcNXrWMY/UzELJF8H+3\ 257 | IrfgG6Iz2v2+xBxY0g/FiHDhFPJVsmEqA0dncAYUB1DypFcciaT9pvfagwjlJEvWNwboBRB1XPBIdSrVdt73D4CL1EOcHkbJq8l5NFAp+cOBXAyB1upuZfEaq0eSgsFIkmslNX3SbI5P8tsHbT08k/WdOb0yYlkQ\ 258 | ituI7ow4Ib9RkNfpF56QQotrLn7BS5Xpsz0UzFOkMilK5umsQck8PTnCmU9P95BSp4/RjaJ5dF63RfP9P2/Ri5X/eLcorvB6pTVZFlubxyb01JeLq09t42AQ5aGxKhaFvocJQIWTtC/N/VmMTWNn4uX/AMpF3Z8=\ 259 | """, 260 | } 261 | 262 | for key, stub in stubs.items(): 263 | code = eval(zlib.decompress(base64.b64decode(stub))) 264 | print("Processing " + key) 265 | print("Text size:", str(len(code["text"])) + " bytes") 266 | print("Data size:", str(len(code["data"])) + " bytes") 267 | 268 | print(code["text"]) 269 | print(base64.b64encode(code["text"])) 270 | code["text"] = base64.b64encode(code["text"]).decode("utf-8") 271 | code["data"] = base64.b64encode(code["data"]).decode("utf-8") 272 | 273 | jsondata = json.dumps(code) 274 | 275 | f = open("../stubs/" + key + ".json", "w+") 276 | f.write(jsondata) 277 | f.close() 278 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | O.MG Web Flasher 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 |
52 | 91 |
92 | 93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |

O.MG Web Flasher

101 |

This tool is used exclusively to flash firmware to O.MG Devices

102 |
103 |

Agreement

104 |

O.MG Cable, O.MG Adapter, O.MG Plug, and O.MG UnBlocker are trademarks of Mischief Gadgets, LLC. Mischief Gadgets, LLC requires that all users read and accept the provisions of the Terms of Use Policy and the Privacy Policy prior to granting users any authorization to use pentesting hardware created by Mischief Gadgets, LLC and/or its affiliates. The Terms of Use Policy and the Privacy Policy can be found at https://o.mg.lol, and must be affirmatively consented to by users prior to using any pentesting hardware created by Mischief Gadgets, LLC and/or its affiliates (hereinafter referred to as “O.MG Devices”). 105 | Reading and Accepting the Terms of Use and the Privacy Policy are REQUIRED CONSIDERATIONS for Mischief Gadgets, LLC and/or its affiliates granting users the right to use any O.MG Device. All persons are DENIED permission to use any O.MG Device, unless they read and affirmatively accept the Terms of Use Policy and the Privacy Policy located at https://o.mg.lol.

106 |

Privacy Policy

107 |
108 |

All persons under the age of 18 are denied access to the website located at https://o.mg.lol, as well as denied authorization to use any O.MG Device. If you are under the age of 18, it is unlawful for you to visit, communicate, or interact with Mischief Gadgets, LLC and/or its affiliates in any manner. Mischief Gadgets, LLC and/or its affiliates specifically denies access to any individual that is covered by the Child Online Privacy Act (COPA) of 1998.

109 |

Mischief Gadgets, LLC and/or its affiliates reserve the right to deny access to any person or viewer for any reason. Under the provisions of this Privacy Policy, Mischief Gadgets, LLC and/or its affiliates are allowed to collect and store data and information for the purpose of exclusion, and for any other uses seen fit.

110 |

Mischief Gadgets, LLC and/or its affiliates have established safeguards to help prevent unauthorized access to or misuse of your information but cannot guarantee that your information will never be disclosed in a manner inconsistent with this Privacy Policy (for example, as a result of any unauthorized act by third parties that violate applicable law or our affiliates’ policies). To protect your privacy and security, we may use passwords or other technologies to register or authenticate you and enable you to take advantage of our services, and before granting access or making corrections to your information. 111 |

Mischief Gadgets, LLC and/or its affiliates do not rent or sell your personally identifiable information (such as name, address, telephone number, and credit card information) to third parties for their marketing purposes.

112 |

This Privacy Policy may change from time to time. Users have an affirmative duty, as part of the consideration for permission to use O.MG Devices, to keep themselves informed of changes to this Privacy Policy. All changes to this Privacy Policy will be posted at https://o.mg.lol.

113 |
114 |

Terms of Use

115 |

Pentesting hardware designed by Mischief Gadgets, LLC and/or its affiliates (hereinafter referred to as “O.MG Devices”) are network administration and pentesting tools used for authorized auditing and security analysis purposes only where permitted, subject to local and international laws where applicable. Users are solely responsible for compliance with all laws of their locality. Mischief Gadgets, LLC and/or its affiliates claim no responsibility for unauthorized or unlawful use.

116 |

O.MG Devices are packaged with a limited warranty, the acceptance of which is a condition of sale. See https://o.mg.lol for additional warranty details and limitations. Availability and performance of certain features, services, and applications are device and network dependent and may not be available in all areas; additional terms, conditions and/or charges may apply.

117 |

You agree not to access or use any O.MG Device or the website located at https://o.mg.lol in any unlawful way or for any unlawful or illegitimate purpose or in any manner that contravenes this Agreement. You shall not use any O.MG Device to post, use, store, or transmit any information that is unlawful, libelous, defamatory, obscene, fraudulent, predatory of minors, harassing, threatening or hateful towards any individual, this includes any information that infringes or violates any of the intellectual property rights of others or the privacy rights of others. You shall not use any O.MG Device to attempt to disturb the peace by any method, including through use of viruses, Trojan horses, worms, time bombs, denial of service attacks, flooding or spamming. You shall not use any O.MG Device in any manner that could damage, disable or impair Mischief Gadgets, LLC and/or its affiliates, or any third-party. You shall not use any O.MG Device to attempt to gain unauthorized access to any user account, computer systems, or networks through hacking, password mining or any other means. You shall not use any O.MG Device alongside any robot, data scraper, miner or virtual computer to gain unlawful access to protected computer systems.

118 |

All features, functionality and other product specifications are subject to change without notice or obligation. Mischief Gadgets, LLC and/or its affiliates reserve the right to make changes to the product description in this document without notice. Mischief Gadgets, LLC and/or its affiliates do not assume any liability that may occur due to the use or application of the product(s) described herein.

119 |

These terms and conditions shall be governed by and construed in accordance with the laws of the state of New York, United States of America, and you agree to submit to the personal jurisdiction of the courts of the state of New York. In the event that any portion of these terms and conditions is deemed by a court to be invalid, the remaining provisions shall remain in full force and effect. You agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to this Web site, or the use of this Website, must be filed within one year after such claim or cause of action arose and must be filed in a court in New York, New York, U.S.A.

120 |

As required by Section 512(c)(2) of Title 17 of the United States Code, if you believe that any material on the website located at https://o.mg.lol infringes your copyright, you must send a notice of claimed infringement to Mischief Gadget, LLC’s General Counsel at the following address:
121 | c/o Mischief Gadgets, LLC - General Counsel
122 | Tor Ekeland Law, PLLC
123 | 30 Wall St., 8th Floor
124 | New York, NY 10005
125 | info@torekeland.com]

126 |

If you do not agree to be bound by this Agreement, do not access or use any O.MG Device, or the website located at https://o.mg.lol. We reserve the right, with or without notice, to make changes to this Agreement at our discretion. Continued use of any O.MG Device or the website located at https://o.mg.lol constitutes your acceptance of these Terms, as they may appear at the time of your access.

127 |

By clicking the “I Agree” button, by availing yourself of any O.MG Device or the website located at https://o.mg.lol, or by accessing, visiting, browsing, using or attempting to interact with or use any O.MG Device or the website located at https://o.mg.lol, you agree that you have read, understand, and agree to be bound by this Agreement as well as our Privacy Policy, which is a part of this Agreement and which can be viewed here: https://o.mg.lol.

128 |
129 |
130 |
131 |
132 |
133 | 140 | 144 | 145 |
146 |
147 | 148 | 149 |
150 | 151 |
152 |
153 |
154 |
155 |

O.MG Web Flasher

156 |
157 | 163 | 168 |
169 |
170 |
171 | 172 |
173 |
174 |

175 | 179 |

180 |
181 |
182 | 183 |
184 |
185 | Welcome Instructions 186 |

The "C+A" Programmer requires a cable to connect to your computer and uses a USB Type C connector so either a C to A or C to C cable to connect the programmer to your computer. The programmer port labeled "PC" should go to your computer. Then plug your O.MG Cable into one of the programmer ports labeled "O.MG". The "A" Programmer connects directly to your computer via a USB Type A Port.

187 |
188 |
189 |
190 |
191 |

192 | 197 |

198 |
199 |
200 | First, connect the O.MG Programmer to your computer. Then connect the active end of the O.MG Device into the programmer. Need help or more details? Click the Help button below. 201 |
202 |
203 | 204 | 205 |
206 |
207 |
208 |
209 |

210 | 215 |

216 |
217 |
218 | Now click the Connect button below. Your browser will prompt you to select the programmer. If no ports are shown, or you get an error, click the Help button below. 219 |
220 |
221 | 222 |
223 |
224 |
225 |
226 |

227 | 232 |

233 |
234 |
235 | Press the Program button below, thats it! Just wait for the progress bar to complete. 236 |
237 | 238 | 241 |
242 |
243 |
244 |
245 |
246 |
247 | 248 |
249 | 250 |
251 |
252 |
253 |
254 | 255 |
256 |
257 |
258 |
259 |
260 |
261 |

O.MG Web Flasher

262 |
263 | 269 |
270 |
271 |
272 |
273 |

Firmware Flashed Successfully!

274 |
275 |
276 |
277 |

278 | WiFi SSID:   Unknown
279 | WiFi Password:   Unknown
280 | Configuration Mode:   Unknown 281 |

282 |
283 |
284 | 285 |

286 |

287 | To use the O.MG Device: Connect the active into a USB port. 288 | Then connect to the WiFi network and browse to http://192.168.4.1/. 289 |
BEFORE USE, FULLY READ THE USAGE & SAFETY INSTRUCTIONS 290 |

291 |
292 | SPECS: 293 |
    294 |
  • Power: 5v (+/- 0.5v), 500mA
  • 295 |
  • Radio: 802.11b/g/n (2.4GHz)
  • 296 |
  • For Devices with passthrough capability (O.MG Cable, O.MG Adapter, & O.MG UnBlocker): 297 |
      298 |
    • Passthrough Charging: 5v (max)
    • 299 |
    • Passthrough Data: 480mbps USB
    • 300 |
    301 |
  • 302 |
303 |
304 |

305 |

306 | Further details about the current flashing process can be seen in the flasher log (button in the top right of this screen). 307 |

308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 | 341 | 434 | 465 | 489 | 511 | 526 | 527 | 528 | 529 | -------------------------------------------------------------------------------- /js/esptool.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let port; 4 | let reader; 5 | let inputStream; 6 | let outputStream; 7 | let inputBuffer = []; 8 | 9 | const ESP_ROM_BAUD = 115200; 10 | const FLASH_WRITE_SIZE = 0x400; 11 | const STUBLOADER_FLASH_WRITE_SIZE = 0x4000; 12 | const FLASH_SECTOR_SIZE = 0x1000; // Flash sector size, minimum unit of erase. 13 | 14 | const SYNC_PACKET = toByteArray("\x07\x07\x12 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"); 15 | const CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000; 16 | const ESP8266 = 0x8266; 17 | const ESP32 = 0x32; 18 | const ESP32S2 = 0x3252; 19 | const ESP32_DATAREGVALUE = 0x15122500; 20 | const ESP8266_DATAREGVALUE = 0x00062000; 21 | const ESP32S2_DATAREGVALUE = 0x500; 22 | 23 | // Commands supported by ESP8266 ROM bootloader 24 | const ESP_FLASH_BEGIN = 0x02; 25 | const ESP_FLASH_DATA = 0x03; 26 | const ESP_FLASH_END = 0x04; 27 | const ESP_MEM_BEGIN = 0x05; 28 | const ESP_MEM_END = 0x06; 29 | const ESP_MEM_DATA = 0x07; 30 | const ESP_SYNC = 0x08; 31 | const ESP_WRITE_REG = 0x09; 32 | const ESP_READ_REG = 0x0A; 33 | 34 | // Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub) 35 | const ESP_SPI_SET_PARAMS = 0x0B; 36 | const ESP_SPI_ATTACH = 0x0D; 37 | const ESP_READ_FLASH_SLOW = 0x0E // ROM only, much slower than the stub flash read 38 | const ESP_CHANGE_BAUDRATE = 0x0F; 39 | const ESP_FLASH_DEFL_BEGIN = 0x10 40 | const ESP_FLASH_DEFL_DATA = 0x11 41 | const ESP_FLASH_DEFL_END = 0x12 42 | const ESP_SPI_FLASH_MD5 = 0x13; 43 | 44 | // Commands supported by ESP32-S2/S3/C3/C6 ROM bootloader only 45 | const ESP_GET_SECURITY_INFO = 0x14; 46 | 47 | // Some commands supported by stub only 48 | const ESP_ERASE_FLASH = 0xD0; 49 | const ESP_ERASE_REGION = 0xD1; 50 | const ESP_READ_FLASH = 0xD2; 51 | const ESP_RUN_USER_CODE = 0xD3; 52 | 53 | // Response code(s) sent by ROM 54 | const ROM_INVALID_RECV_MSG = 0x05; 55 | 56 | // Initial state for the checksum routine 57 | const ESP_CHECKSUM_MAGIC = 0xEF; 58 | 59 | 60 | const UART_DATE_REG_ADDR = 0x60000078; 61 | 62 | const USB_RAM_BLOCK = 0x800; 63 | const ESP_RAM_BLOCK = 0x1800; 64 | 65 | // Timeouts 66 | const DEFAULT_TIMEOUT = 3000; 67 | const CHIP_ERASE_TIMEOUT = 120000; // timeout for full chip erase in ms 68 | const MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2; // longest any command can run in ms 69 | const SYNC_TIMEOUT = 100; // timeout for syncing with bootloader in ms 70 | const ERASE_REGION_TIMEOUT_PER_MB = 30000; // timeout (per megabyte) for erasing a region in ms 71 | const MEM_END_ROM_TIMEOUT = 500; 72 | 73 | 74 | const magicValues = { 75 | "ESP8266": { "chipId": ESP8266, "magicVal": 0xfff0c101}, 76 | "ESP32": { "chipId": ESP32, "magicVal": 0x00f01d83}, 77 | "ESP32S2": { "chipId": ESP32S2, "magicVal": 0x000007c6} 78 | } 79 | 80 | 81 | class EspLoader { 82 | constructor(params) { 83 | this._chipfamily = null; 84 | this.readTimeout = 3000; // Arbitrary number for now. This should be set more dynamically in the sendCommand function 85 | this._efuses = new Array(4).fill(0); 86 | this._flashsize = 4 * 1024 * 1024; 87 | this.currFile = 0; 88 | if (this.isFunction(params.updateProgress)) { 89 | this.updateProgress = params.updateProgress 90 | } else { 91 | this.updateProgress = null 92 | } 93 | 94 | if (this.isFunction(params.logMsg)) { 95 | this.logMsg = params.logMsg 96 | } else { 97 | this.logMsg = console.log 98 | } 99 | this.debug = false; 100 | if (this.isFunction(params.debugMsg)) { 101 | if (params.debug !== false) { 102 | this.debug = true; 103 | } 104 | this._debugMsg = params.debugMsg 105 | } else { 106 | this._debugMsg = this.logMsg() 107 | } 108 | this.IS_STUB = false; 109 | this.syncStubDetected = false; 110 | } 111 | 112 | isFunction(functionObj) { 113 | return functionObj && {}.toString.call(functionObj) === '[object Function]'; 114 | } 115 | 116 | toHex(value, size=2) { 117 | return "0x" + value.toString(16).toUpperCase().padStart(size, "0"); 118 | } 119 | 120 | getChromeVersion() { 121 | let raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); 122 | 123 | return raw ? parseInt(raw[2], 10) : false; 124 | } 125 | 126 | /** 127 | * @name slipEncode 128 | * Take an array buffer and return back a new array where 129 | * 0xdb is replaced with 0xdb 0xdd and 0xc0 is replaced with 0xdb 0xdc 130 | */ 131 | slipEncode(buffer) { 132 | let encoded = [0xC0]; 133 | for (let byte of buffer) { 134 | if (byte == 0xDB) { 135 | encoded = encoded.concat([0xDB, 0xDD]); 136 | } else if (byte == 0xC0) { 137 | encoded = encoded.concat([0xDB, 0xDC]); 138 | } else { 139 | encoded.push(byte); 140 | } 141 | } 142 | encoded.push(0xC0); 143 | return encoded; 144 | }; 145 | 146 | /** 147 | * @name macAddr 148 | * The MAC address burned into the OTP memory of the ESP chip 149 | */ 150 | macAddr() { 151 | let macAddr = new Array(6).fill(0); 152 | let mac0 = this._efuses[0]; 153 | let mac1 = this._efuses[1]; 154 | let mac2 = this._efuses[2]; 155 | let mac3 = this._efuses[3]; 156 | let oui; 157 | if (this._chipfamily == ESP8266) { 158 | if (mac3 != 0) { 159 | oui = [(mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF]; 160 | } else if (((mac1 >> 16) & 0xFF) == 0) { 161 | oui = [0x18, 0xFE, 0x34]; 162 | } else if (((mac1 >> 16) & 0xFF) == 1) { 163 | oui = [0xAC, 0xD0, 0x74]; 164 | } else { 165 | throw("Couldnt determine OUI"); 166 | } 167 | 168 | macAddr[0] = oui[0]; 169 | macAddr[1] = oui[1]; 170 | macAddr[2] = oui[2]; 171 | macAddr[3] = (mac1 >> 8) & 0xFF; 172 | macAddr[4] = mac1 & 0xFF; 173 | macAddr[5] = (mac0 >> 24) & 0xFF; 174 | } else if (this._chipfamily == ESP32) { 175 | macAddr[0] = mac2 >> 8 & 0xFF; 176 | macAddr[1] = mac2 & 0xFF; 177 | macAddr[2] = mac1 >> 24 & 0xFF; 178 | macAddr[3] = mac1 >> 16 & 0xFF; 179 | macAddr[4] = mac1 >> 8 & 0xFF; 180 | macAddr[5] = mac1 & 0xFF; 181 | } else if (this._chipfamily == ESP32S2) { 182 | macAddr[0] = mac2 >> 8 & 0xFF; 183 | macAddr[1] = mac2 & 0xFF; 184 | macAddr[2] = mac1 >> 24 & 0xFF; 185 | macAddr[3] = mac1 >> 16 & 0xFF; 186 | macAddr[4] = mac1 >> 8 & 0xFF; 187 | macAddr[5] = mac1 & 0xFF; 188 | } else { 189 | throw("Unknown chip family") 190 | } 191 | return macAddr; 192 | }; 193 | 194 | debugMsg(...values) { 195 | if (this.debug) { 196 | this._debugMsg(...values); 197 | } 198 | } 199 | 200 | /** 201 | * @name _readEfuses 202 | * Read the OTP data for this chip and store into this.efuses array 203 | */ 204 | async _readEfuses() { 205 | let baseAddr 206 | if (this._chipfamily == ESP8266) { 207 | baseAddr = 0x3FF00050; 208 | } else if (this._chipfamily == ESP32) { 209 | baseAddr = 0x3FF5A000; 210 | } else if (this._chipfamily == ESP32S2) { 211 | baseAddr = 0x6001A000; 212 | } else { 213 | throw("Don't know what chip this is"); 214 | } 215 | for (let i = 0; i < 4; i++) { 216 | this._efuses[i] = await this.readRegister(baseAddr + 4 * i); 217 | } 218 | }; 219 | 220 | /** 221 | * @name readRegister 222 | * Read a register within the ESP chip RAM, returns a 4-element list 223 | */ 224 | async readRegister(reg) { 225 | if (this.debug) { 226 | this.debugMsg("Reading from Register " + this.toHex(reg, 8)); 227 | } 228 | let packet = struct.pack(" setTimeout(resolve, ms)); 248 | } 249 | 250 | /** 251 | * @name chipType 252 | * ESP32 or ESP8266 based on which chip type we're talking to 253 | */ 254 | async chipType() { 255 | if (this._chipfamily === null) { 256 | this._chipfamily = await this.detectChip() 257 | } 258 | return this._chipfamily; 259 | }; 260 | 261 | 262 | async detectChip() { 263 | let chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR); 264 | 265 | // Loop through magicValues and if the value matches, then the key is the chip ID 266 | for (const [key, value] of Object.entries(magicValues)) { 267 | if (chipMagicValue == value["magicVal"]) { 268 | return value["chipId"] 269 | } 270 | } 271 | this.logMsg("Detection failed and WebFlasher cannot continue."); 272 | throw("Unable to detect OMG Device. If you are using a v1 Programmer from 2020 (lacks USB-C) please use the Python Flasher for now!
Otherwise, click the Help button below for common fixes & refresh this page to attempt flashing again."); 273 | } 274 | 275 | /** 276 | * @name chipType 277 | * The specific name of the chip, e.g. ESP8266EX, to the best 278 | * of our ability to determine without a stub bootloader. 279 | */ 280 | async chipName() { 281 | let chipType = await this.chipType(); 282 | await this._readEfuses(); 283 | 284 | if (chipType == ESP32) { 285 | return "ESP32"; 286 | } 287 | if (chipType == ESP32S2) { 288 | return "ESP32-S2"; 289 | } 290 | if (chipType == ESP8266) { 291 | if (this._efuses[0] & (1 << 4) || this._efuses[2] & (1 << 16)) { 292 | return "ESP8285"; 293 | } 294 | return "ESP8266EX"; 295 | } 296 | return null; 297 | }; 298 | 299 | /** 300 | * @name checkCommand 301 | * Send a command packet, check that the command succeeded and 302 | * return a tuple with the value and data. 303 | * See the ESP Serial Protocol for more details on what value/data are 304 | */ 305 | async checkCommand(opcode, buffer, checksum=0, timeout=DEFAULT_TIMEOUT) { 306 | timeout = Math.min(timeout, MAX_TIMEOUT); 307 | await this.sendCommand(opcode, buffer, checksum); 308 | let [value, data] = await this.getResponse(opcode, timeout); 309 | let statusLen; 310 | if (data !== null) { 311 | if (this.IS_STUB) { 312 | statusLen = 2; 313 | } else if (this._chipfamily == ESP8266) { 314 | statusLen = 2; 315 | } else if ([ESP32, ESP32S2].includes(this._chipfamily)) { 316 | statusLen = 4; 317 | } else { 318 | if ([2, 4].includes(data.length)) { 319 | statusLen = data.length; 320 | } 321 | } 322 | } 323 | 324 | if (data === null || data.length < statusLen) { 325 | this.logMsg("Error, flashing failed, please reload web page and try again"); 326 | this.logMsg(" "); 327 | throw("Didn't get enough status bytes"); 328 | } 329 | let status = data.slice(-statusLen, data.length); 330 | data = data.slice(0, -statusLen); 331 | if (this.debug) { 332 | this.debugMsg("status", status); 333 | this.debugMsg("value", value); 334 | this.debugMsg("data", data); 335 | } 336 | if (status[0] == 1) { 337 | if (status[1] == ROM_INVALID_RECV_MSG) { 338 | throw("Invalid (unsupported) command " + this.toHex(opcode)); 339 | } else { 340 | throw("Command failure error code " + this.toHex(status[1])); 341 | } 342 | } 343 | 344 | if (data.length > 0) { 345 | return data; 346 | } 347 | return value; 348 | }; 349 | 350 | /** 351 | * @name timeoutPerMb 352 | * Scales timeouts which are size-specific 353 | */ 354 | timeoutPerMb(secondsPerMb, sizeBytes) { 355 | let result = Math.floor(secondsPerMb * (sizeBytes / 0x1e6)); 356 | if (result < DEFAULT_TIMEOUT) { 357 | return DEFAULT_TIMEOUT; 358 | } 359 | return result; 360 | }; 361 | 362 | /** 363 | * @name sendCommand 364 | * Send a slip-encoded, checksummed command over the UART, 365 | * does not check response 366 | */ 367 | async sendCommand(opcode, buffer, checksum=0) { 368 | //inputBuffer = []; // Reset input buffer 369 | let packet = struct.pack(" this.toHex(value)).join(", ") + "]" 447 | } 448 | 449 | /** 450 | * @name readPacket 451 | * Generator to read SLIP packets from a serial port. 452 | * Yields one full SLIP packet at a time, raises exception on timeout or invalid data. 453 | * Designed to avoid too many calls to serial.read(1), which can bog 454 | * down on slow systems. 455 | */ 456 | 457 | async readPacket() { 458 | let partialPacket = null; 459 | let inEscape = false; 460 | let readBytes = []; 461 | while (true) { 462 | let stamp = Date.now(); 463 | readBytes = []; 464 | while (Date.now() - stamp < this.readTimeout) { 465 | if (inputBuffer.length > 0) { 466 | readBytes.push(inputBuffer.shift()); 467 | break; 468 | } else { 469 | await this.sleep(10); 470 | } 471 | } 472 | if (readBytes.length == 0) { 473 | let waitingFor = partialPacket === null ? "header" : "content"; 474 | this.debugMsg("Timed out waiting for packet " + waitingFor); 475 | throw new SlipReadError("Timed out waiting for packet " + waitingFor); 476 | } 477 | this.debugMsg("Read " + readBytes.length + " bytes: " + this.hexFormatter(readBytes)); 478 | for (let b of readBytes) { 479 | if (partialPacket === null) { // waiting for packet header 480 | if (b == 0xc0) { 481 | partialPacket = []; 482 | } else { 483 | this.debugMsg("Read invalid data: " + this.hexFormatter(readBytes)); 484 | this.debugMsg("Remaining data in serial buffer: " + this.hexFormatter(inputBuffer)); 485 | throw new SlipReadError('Invalid head of packet (' + this.toHex(b) + ')'); 486 | } 487 | } else if (inEscape) { // part-way through escape sequence 488 | inEscape = false; 489 | if (b == 0xdc) { 490 | partialPacket.push(0xc0); 491 | } else if (b == 0xdd) { 492 | partialPacket.push(0xdb); 493 | } else { 494 | this.debugMsg("Read invalid data: " + this.hexFormatter(readBytes)); 495 | this.debugMsg("Remaining data in serial buffer: " + this.hexFormatter(inputBuffer)); 496 | throw new SlipReadError('Invalid SLIP escape (0xdb, ' + this.toHex(b) + ')'); 497 | } 498 | } else if (b == 0xdb) { // start of escape sequence 499 | inEscape = true; 500 | } else if (b == 0xc0) { // end of packet 501 | this.debugMsg("Received full packet: " + this.hexFormatter(partialPacket)) 502 | return partialPacket; 503 | partialPacket = null; 504 | } else { // normal byte in packet 505 | partialPacket.push(b); 506 | } 507 | } 508 | } 509 | return ''; 510 | } 511 | 512 | /** 513 | * @name getResponse 514 | * Read response data and decodes the slip packet, then parses 515 | * out the value/data and returns as a tuple of (value, data) where 516 | * each is a list of bytes 517 | */ 518 | async getResponse(opcode, timeout=DEFAULT_TIMEOUT) { 519 | this.readTimeout = timeout; 520 | let packet; 521 | let packetLength = 0; 522 | let resp, opRet, lenRet, val, data; 523 | for (let i = 0; i < 100; i++) { 524 | try { 525 | packet = await this.readPacket(); 526 | } catch(e) { 527 | this.logMsg("Timed out after " + this.readTimeout + " milliseconds"); 528 | return [null, null]; 529 | } 530 | 531 | if (packet.length < 8) { 532 | continue; 533 | } 534 | 535 | [resp, opRet, lenRet, val] = struct.unpack(' 1 && data[0] == 0 && data[1] == 0) { 652 | return true; 653 | } 654 | } 655 | return false; 656 | }; 657 | 658 | async getFlashID(){ 659 | // try to read data if its unset 660 | if(!this._flash_size){ 661 | console.log(this) 662 | if(this._efuses[0] == 0 && this._efuses[1] == 0 && this._efuses[3] == 0){ 663 | //await this._readEfuses(); 664 | this.logMsg("Unable to fetch Chip ID"); 665 | } 666 | let lfuse=this._efuses[3]; 667 | console.log(this._efuses); 668 | // try to read one more time before doing defaults 669 | var mem_size; 670 | if(lfuse===undefined){ 671 | mem_size=0x0; 672 | } else { 673 | mem_size = (lfuse&0xFF000000)>>24; 674 | } 675 | let calculated_mem = 1; 676 | switch(mem_size){ 677 | case (0x4): 678 | calculated_mem = 2; 679 | break; 680 | case (0x1): 681 | case (0x0): 682 | calculated_mem = 1; 683 | throw("Your O.MG Device needs to use the Advanced Flasher. Please follow the Advanced Flasher Guidea>"); 684 | break; 685 | } 686 | this._flash_size = (0x400*calculated_mem); 687 | this._flashsize = (1024*1024*calculated_mem); 688 | // initial set 689 | //FLASH_WRITE_SIZE=this._flash_size; 690 | //STUBLOADER_FLASH_WRITE_SIZE=this._flash_size; 691 | //FLASH_SECTOR_SIZE=this._flash_size; 692 | } 693 | let m = this._flash_size; 694 | console.log("detected memory is " + m); 695 | return m; 696 | } 697 | 698 | async getFlashMB(){ 699 | let flash_id = await this.getFlashID(); 700 | return (flash_id/1024) + " MB"; 701 | } 702 | 703 | /** 704 | * @name getFlashWriteSize 705 | * Get the Flash write size based on the chip 706 | */ 707 | getFlashWriteSize() { 708 | return this.getFlashID(); 709 | }; 710 | 711 | 712 | /** 713 | * @name flashData 714 | * Program a full, uncompressed binary file into SPI Flash at 715 | * a given offset. If an ESP32 and md5 string is passed in, will also 716 | * verify memory. ESP8266 does not have checksum memory verification in 717 | * ROM 718 | */ 719 | async flashData(binaryData, offset=0, part=0) { 720 | let filesize = binaryData.byteLength; 721 | this.logMsg("\nWriting data with filesize: " + filesize); 722 | let blocks = await this.flashBegin(filesize, offset); 723 | let block = []; 724 | let seq = 0; 725 | let written = 0; 726 | let address = offset; 727 | let position = 0; 728 | let stamp = Date.now(); 729 | let flashWriteSize = await this.getFlashWriteSize(); 730 | 731 | while (filesize - position > 0) { 732 | let percentage = Math.floor(100 * (seq + 1) / blocks); 733 | this.logMsg( 734 | "Writing at " + this.toHex(address + seq * flashWriteSize, 8) + "... (" + percentage + " %)" 735 | ); 736 | this.updateProgress(this.currFile,percentage); 737 | if (filesize - position >= flashWriteSize) { 738 | block = Array.from(new Uint8Array(binaryData, position, flashWriteSize)); 739 | } else { 740 | // Pad the last block 741 | block = Array.from(new Uint8Array(binaryData, position, filesize - position)); 742 | block = block.concat(new Array(flashWriteSize - block.length).fill(0xFF)); 743 | } 744 | await this.flashBlock(block, seq); 745 | // add a delay for sanity (FIX) 746 | await this.sleep(120); 747 | seq += 1; 748 | written += block.length; 749 | position += flashWriteSize; 750 | } 751 | this.logMsg("Took " + (Date.now() - stamp) + "ms to write " + filesize + " bytes"); 752 | this.currFile+=1; 753 | }; 754 | 755 | /** 756 | * @name flashBlock 757 | * Send one block of data to program into SPI Flash memory 758 | */ 759 | async flashBlock(data, seq, timeout=DEFAULT_TIMEOUT) { 760 | await this.checkCommand( 761 | ESP_FLASH_DATA, 762 | struct.pack(" start) { 862 | throw("Software loader is resident at " + this.toHex(start, 8) + "-" + this.toHex(end, 8) + ". " + 863 | "Can't load binary at overlapping address range " + this.toHex(load_start, 8) + "-" + this.toHex(load_end, 8) + ". " + 864 | "Try changing the binary loading address."); 865 | } 866 | } 867 | } 868 | 869 | return this.checkCommand(ESP_MEM_BEGIN, struct.pack(' setTimeout(resolve, 100)); 912 | await port.setSignals({ 913 | dataTerminalReady: r, 914 | requestToSend: false, 915 | break: false, 916 | }); 917 | await new Promise((resolve) => setTimeout(resolve, 500)); 918 | } 919 | 920 | async preferredReset(r = false) { 921 | logMsg("Power Off...") 922 | await port.setSignals({ 923 | dataTerminalReady: false, 924 | requestToSend: true, 925 | }); 926 | await new Promise((resolve) => setTimeout(resolve, 2000)); 927 | await port.setSignals({ 928 | dataTerminalReady: r, 929 | requestToSend: false, 930 | //break: false, 931 | }); 932 | await new Promise((resolve) => setTimeout(resolve, 1000)) 933 | } 934 | 935 | 936 | async getStubCode() { 937 | let response = await fetch('stubs/' + this.getStubFile() + '.json'); 938 | let stubcode = await response.json(); 939 | 940 | // Base64 decode the text and data 941 | stubcode.text = toByteArray(atob(stubcode.text)); 942 | stubcode.data = toByteArray(atob(stubcode.data)); 943 | return stubcode; 944 | } 945 | 946 | getStubFile() { 947 | if (this._chipfamily == ESP32) { 948 | return "esp32"; 949 | } else if (this._chipfamily == ESP32S2) { 950 | return "esp32s2"; 951 | } else if (this._chipfamily == ESP8266) { 952 | return "esp8266"; 953 | } 954 | } 955 | 956 | getStubLoaderClass() { 957 | // Based on current chip, we return the appropriate stub loader class 958 | } 959 | 960 | getRomClass() { 961 | // Based on current chip, we return the appropriate Rom class 962 | } 963 | 964 | async runStub(stub=null) { 965 | if (stub === null) { 966 | stub = await this.getStubCode(); 967 | } 968 | 969 | if (this.syncStubDetected) { 970 | this.logMsg("Stub is already running. No upload is necessary."); 971 | return this.stubClass; 972 | } 973 | 974 | let ramBlock = ESP_RAM_BLOCK; 975 | // We're transferring over USB, right? 976 | if ([ESP32S2].includes(this._chipfamily)) { 977 | ramBlock = USB_RAM_BLOCK; 978 | } 979 | 980 | // Upload 981 | this.logMsg("Uploading stub...") 982 | for (let field of ['text', 'data']) { 983 | if (Object.keys(stub).includes(field)) { 984 | let offset = stub[field + "_start"]; 985 | let length = stub[field].length; 986 | let blocks = Math.floor((length + ramBlock - 1) / ramBlock); 987 | await this.memBegin(length, blocks, ramBlock, offset); 988 | for (let seq of Array(blocks).keys()) { 989 | let fromOffs = seq * ramBlock; 990 | let toOffs = fromOffs + ramBlock; 991 | if (toOffs > length) { 992 | toOffs = length; 993 | } 994 | await this.memBlock(stub[field].slice(fromOffs, toOffs), seq); 995 | } 996 | } 997 | } 998 | this.logMsg("Running stub...") 999 | await this.memFinish(stub['entry']); 1000 | 1001 | let p = await this.readBuffer(500); 1002 | p = String.fromCharCode(...p); 1003 | 1004 | if (p != 'OHAI') { 1005 | throw "Failed to start stub. Unexpected response: " + p; 1006 | } 1007 | this.logMsg("Stub is now running..."); 1008 | this.stubClass = new EspStubLoader({ 1009 | updateProgress: this.updateProgress, 1010 | logMsg: this.logMsg, 1011 | debugMsg: this._debugMsg, 1012 | debug: this.debug, 1013 | flash_size: this._flash_size, 1014 | efuses: this._efuses 1015 | }); 1016 | return this.stubClass; 1017 | } 1018 | } 1019 | 1020 | class EspStubLoader extends EspLoader { 1021 | /* 1022 | The Stubloader has commands that run on the uploaded Stub Code in RAM 1023 | rather than built in commands. 1024 | */ 1025 | constructor(params) { 1026 | super(params); 1027 | this.IS_STUB = true; 1028 | } 1029 | /** 1030 | * @name eraseFlash 1031 | * depending on flash chip model the erase may take this long (maybe longer!) 1032 | */ 1033 | async eraseFlash() { 1034 | await this.checkCommand(ESP_ERASE_FLASH, [], 0, CHIP_ERASE_TIMEOUT); 1035 | }; 1036 | 1037 | /** 1038 | * @name getFlashWriteSize 1039 | * Get the Flash write size based on the chip 1040 | */ 1041 | getFlashWriteSize() { 1042 | return this.getFlashID(); 1043 | }; 1044 | 1045 | } 1046 | 1047 | class Esp32StubLoader extends EspStubLoader { 1048 | 1049 | } 1050 | 1051 | /* 1052 | Represents error when NVS Partition size given is insufficient 1053 | to accomodate the data in the given csv file 1054 | */ 1055 | class SlipReadError extends Error { 1056 | constructor(message) { 1057 | super(message); 1058 | this.name = "SlipReadError"; 1059 | } 1060 | } 1061 | -------------------------------------------------------------------------------- /js/utilities.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name toByteArray 3 | * Convert a string to a byte array 4 | */ 5 | function toByteArray(str) { 6 | let byteArray = []; 7 | for (let i = 0; i < str.length; i++) { 8 | let charcode = str.charCodeAt(i); 9 | if (charcode <= 0xFF) { 10 | byteArray.push(charcode); 11 | } 12 | } 13 | return byteArray; 14 | } 15 | 16 | function fromByteArray(byteArray) { 17 | return String.fromCharCode.apply(String, byteArray); 18 | } 19 | 20 | function crc32(data, value=0) { 21 | if (data instanceof Array) { 22 | data = fromByteArray(data); 23 | } 24 | let table = []; 25 | for(let entry, c = 0; c < 256; c++) { 26 | entry = c; 27 | for(let k = 0; k < 8; k++) { 28 | entry = 1 & entry ? 3988292384^entry >>> 1 : entry >>> 1; 29 | } 30 | table[c] = entry; 31 | } 32 | let n = -1 - value; 33 | for(let t = 0; t < data.length; t++) { 34 | n = n >>> 8^table[255 & (n^data.charCodeAt(t))]; 35 | } 36 | return (-1 ^ n) >>> 0; 37 | } 38 | 39 | function zipLongest() { 40 | var args = [].slice.call(arguments); 41 | var longest = args.reduce(function(a,b){ 42 | return a.length > b.length ? a : b 43 | }, []); 44 | 45 | return longest.map(function(_,i){ 46 | return args.map(function(array){return array[i]}) 47 | }); 48 | } 49 | 50 | class struct { 51 | static lut = { 52 | "b": {u: DataView.prototype.getInt8, p: DataView.prototype.setInt8, bytes: 1}, 53 | "B": {u: DataView.prototype.getUint8, p: DataView.prototype.setUint8, bytes: 1}, 54 | "h": {u: DataView.prototype.getInt16, p: DataView.prototype.setInt16, bytes: 2}, 55 | "H": {u: DataView.prototype.getUint16, p: DataView.prototype.setUint16, bytes: 2}, 56 | "i": {u: DataView.prototype.getInt32, p: DataView.prototype.setInt32, bytes: 4}, 57 | "I": {u: DataView.prototype.getUint32, p: DataView.prototype.setUint32, bytes: 4}, 58 | "q": {u: DataView.prototype.getInt64, p: DataView.prototype.setInt64, bytes: 8}, 59 | "Q": {u: DataView.prototype.getUint64, p: DataView.prototype.setUint64, bytes: 8}, 60 | } 61 | 62 | static pack(...args) { 63 | let format = args[0]; 64 | let pointer = 0; 65 | let data = args.slice(1); 66 | if (format.replace(/[<>]/, '').length != data.length) { 67 | throw("Pack format to Argument count mismatch"); 68 | return; 69 | } 70 | let bytes = []; 71 | let littleEndian = true; 72 | for (let i = 0; i < format.length; i++) { 73 | if (format[i] == "<") { 74 | littleEndian = true; 75 | } else if (format[i] == ">") { 76 | littleEndian = false; 77 | } else { 78 | pushBytes(format[i], data[pointer]); 79 | pointer++; 80 | } 81 | } 82 | 83 | function pushBytes(formatChar, value) { 84 | if (!(formatChar in struct.lut)) { 85 | throw("Unhandled character '" + formatChar + "' in pack format"); 86 | } 87 | let dataSize = struct.lut[formatChar].bytes; 88 | let view = new DataView(new ArrayBuffer(dataSize)); 89 | let dataViewFn = struct.lut[formatChar].p.bind(view); 90 | dataViewFn(0, value, littleEndian); 91 | for (let i = 0; i < dataSize; i++) { 92 | bytes.push(view.getUint8(i)); 93 | } 94 | } 95 | 96 | return bytes; 97 | }; 98 | 99 | static unpack(format, bytes) { 100 | let pointer = 0; 101 | let data = []; 102 | let littleEndian = true; 103 | 104 | for (let c of format) { 105 | if (c == "<") { 106 | littleEndian = true; 107 | } else if (c == ">") { 108 | littleEndian = false; 109 | } else { 110 | pushData(c); 111 | } 112 | } 113 | 114 | function pushData(formatChar) { 115 | if (!(formatChar in struct.lut)) { 116 | throw("Unhandled character '" + formatChar + "' in unpack format"); 117 | } 118 | let dataSize = struct.lut[formatChar].bytes; 119 | let view = new DataView(new ArrayBuffer(dataSize)); 120 | for (let i = 0; i < dataSize; i++) { 121 | view.setUint8(i, bytes[pointer + i] & 0xFF); 122 | } 123 | let dataViewFn = struct.lut[formatChar].u.bind(view); 124 | data.push(dataViewFn(0, littleEndian)); 125 | pointer += dataSize; 126 | } 127 | 128 | return data; 129 | }; 130 | } 131 | 132 | String.prototype.replaceAt = function(index, character) { 133 | return this.substr(0, index) + character + this.substr(index + character.length); 134 | }; 135 | 136 | Array.prototype.replaceAt = function(index, newArray) { 137 | return this.slice(0, index).concat(newArray).concat(this.slice(index + newArray.length)); 138 | }; 139 | 140 | function* makeFileIterator(content) { 141 | for (let line of content.split(/\r?\n/)) { 142 | yield line.trim(); 143 | } 144 | return ''; 145 | } 146 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "O.MG WebFlasher", 3 | "short_name": "WebFlasher", 4 | "theme_color": "#ff0000", 5 | "background_color": "#fff", 6 | "display": "minimal-ui", 7 | "orientation": "landscape", 8 | "scope": "/", 9 | "start_url": "/", 10 | "icons": [ 11 | { 12 | "src": "assets/icons/icon-72x72.png", 13 | "sizes": "72x72", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "assets/icons/icon-96x96.png", 18 | "sizes": "96x96", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "assets/icons/icon-128x128.png", 23 | "sizes": "128x128", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "assets/icons/icon-144x144.png", 28 | "sizes": "144x144", 29 | "type": "image/png" 30 | }, 31 | { 32 | "src": "assets/icons/icon-152x152.png", 33 | "sizes": "152x152", 34 | "type": "image/png" 35 | }, 36 | { 37 | "src": "assets/icons/icon-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "assets/icons/icon-384x384.png", 43 | "sizes": "384x384", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "assets/icons/icon-512x512.png", 48 | "sizes": "512x512", 49 | "type": "image/png" 50 | } 51 | ] 52 | } 53 | 54 | -------------------------------------------------------------------------------- /stubs/esp32.json: -------------------------------------------------------------------------------- 1 | {"text": "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", "text_start": 1074520064, "entry": 1074521516, "data": "CMD8Pw==", "data_start": 1073605544} -------------------------------------------------------------------------------- /stubs/esp32c3.json: -------------------------------------------------------------------------------- 1 | {"text": "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", "text_start": 1077411840, "entry": 1077413488, "data": "DEDIPw==", "data_start": 1070164904} -------------------------------------------------------------------------------- /stubs/esp32h2.json: -------------------------------------------------------------------------------- 1 | {"text": "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", "text_start": 1077411840, "entry": 1077413328, "data": "DEDIPw==", "data_start": 1070164904} -------------------------------------------------------------------------------- /stubs/esp32s2.json: -------------------------------------------------------------------------------- 1 | {"text": "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", "text_start": 1073905664, "entry": 1073907548, "data": "CAD9Pw==", "data_start": 1073622004} -------------------------------------------------------------------------------- /stubs/esp32s3.json: -------------------------------------------------------------------------------- 1 | {"text": "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", "text_start": 1077379072, "entry": 1077381492, "data": "CADKPw==", "data_start": 1070279668} -------------------------------------------------------------------------------- /stubs/esp8266.json: -------------------------------------------------------------------------------- 1 | {"entry": 1074843652, "data": "CIH+PwUFBAACAwcAAwMLALnXEEDv1xBAHdgQQLrYEEBo5xBAHtkQQHTZEEDA2RBAaOcQQILaEED/2hBAwNsQQGjnEEBo5xBAWNwQQGjnEEA33xBAAOAQQDvgEEBo5xBAaOcQQNfgEEBo5xBAv+EQQGXiEECj4xBAY+QQQDTlEEBo5xBAaOcQQGjnEEBo5xBAYuYQQGjnEEBX5xBAkN0QQI/YEECm5RBAq9oQQPzZEEBo5xBA7OYQQDHnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQCLaEEBf2hBAvuUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", "text": "qBAAQAH//0Z0AAAAkIH/PwgB/z+AgAAAhIAAAEBAAABIQf8/lIH/PzH5/xLB8CAgdAJhA8XvATKv/pZyA1H0/0H2/zH0/yAgdDA1gEpVwCAAaANCFQBAMPQbQ0BA9MAgAEJVADo2wCAAIkMAIhUAMev/ICD0N5I/Ieb/Meb/Qen/OjLAIABoA1Hm/yeWEoYAAAAAAMAgACkEwCAAWQNGAgDAIABZBMAgACkDMdv/OiIMA8AgADJSAAgxEsEQDfAAoA0AAJiB/z8Agf4/T0hBSais/z+krP8/KNAQQEzqEEAMAABg//8AAAAQAAAAAAEAAAAAAYyAAAAQQAAAAAD//wBAAAAAgf4/BIH+PxAnAAAUAABg//8PAKis/z8Igf4/uKz/PwCAAAA4KQAAkI//PwiD/z8Qg/8/rKz/P5yv/z8wnf8/iK//P5gbAAAACAAAYAkAAFAOAABQEgAAPCkAALCs/z+0rP8/1Kr/PzspAADwgf8/DK//P5Cu/z+ACwAAEK7/P5Ct/z8BAAAAAAAAALAVAADx/wAAmKz/P5iq/z+8DwBAiA8AQKgPAEBYPwBAREYAQCxMAEB4SABAAEoAQLRJAEDMLgBA2DkAQEjfAECQ4QBATCYAQIRJAEAhvP+SoRCQEcAiYSMioAACYUPCYULSYUHiYUDyYT8B6f/AAAAhsv8xs/8MBAYBAABJAksiNzL4xbUBIqCMDEMqIQWoAcW0ASF8/8F6/zGr/yoswCAAyQIhqP8MBDkCMaj/DFIB2f/AAAAxpv8ioQHAIABIAyAkIMAgACkDIqAgAdP/wAAAAdL/wAAAAdL/wAAAcZ3/UZ7/QZ7/MZ7/YqEADAIBzf/AAAAhnP8xYv8qI8AgADgCFnP/wCAA2AIMA8AgADkCDBIiQYQiDQEMJCJBhUJRQzJhIiaSCRwzNxIghggAAAAiDQMyDQKAIhEwIiBmQhEoLcAgACgCImEiBgEAHCIiUUPFqAEioIQMgxoiRZsBIg0DMg0CgCIRMDIgIX//N7ITIqDABZYBIqDuhZUBBaYBRtz/AAAiDQEMtEeSAgaZACc0Q2ZiAsbLAPZyIGYyAoZxAPZCCGYiAsZWAEbKAGZCAgaHAGZSAsarAIbGACaCefaCAoarAAyUR5ICho8AZpICBqMABsAAHCRHkgJGfAAnNCcM9EeSAoY+ACc0CwzUR5IChoMAxrcAAGayAkZLABwUR5ICRlgARrMAQqDRRxJoJzQRHDRHkgJGOABCoNBHEk/GrAAAQqDSR5IChi8AMqDTN5ICRpcFRqcALEIMDieTAgZqBUYrACKgAIWIASKgAEWIAcWYAYWYASKghDKgCBoiC8zFigFW3P0MDs0ORpsAAMwThl8FRpUAJoMCxpMABmAFAWn/wAAA+sycIsaPAAAAICxBAWb/wAAAVhIj8t/w8CzAzC+GaQUAIDD0VhP+4Sv/hgMAICD1AV7/wAAAVtIg4P/A8CzA9z7qhgMAICxBAVf/wAAAVlIf8t/w8CzAVq/+RloFJoOAxgEAAABmswJG3f8MDsKgwIZ4AAAAZrMCRkQFBnIAAMKgASazAgZwACItBDEX/+KgAMKgwiezAsZuADhdKC2FdgFGPAUAwqABJrMChmYAMi0EIQ7/4qAAwqDCN7ICRmUAKD0MHCDjgjhdKC3FcwEx9/4MBEljMtMr6SMgxIMGWgAAIfP+DA5CAgDCoMbnlALGWADIUigtMsPwMCLAQqDAIMSTIs0YTQJioO/GAQBSBAAbRFBmMCBUwDcl8TINBVINBCINBoAzEQAiEVBDIEAyICINBwwOgCIBMCIgICbAMqDBIMOThkMAAAAh2f4MDjICAMKgxueTAsY+ADgywqDI5xMCBjwA4kIAyFIGOgAcggwODBwnEwIGNwAGCQVmQwKGDwVGMAAwIDQMDsKgwOcSAoYwADD0QYvtzQJ888YMACg+MmExAQL/wAAASC4oHmIuACAkEDIhMSYEDsAgAFImAEBDMFBEEEAiIMAgACkGG8zizhD3PMjGgf9mQwJGgP8Gov9mswIG+QTGFgAAAGHA/gwOSAYMFTLD8C0OQCWDMF6DUCIQwqDG55JLcbn+7QKIB8KgyTc4PjBQFMKgwKLNGIzVBgwAWiooAktVKQRLRAwSUJjANzXtFmLaSQaZB8Zn/2aDAoblBAwcDA7GAQAAAOKgAMKg/8AgdAVfAeAgdMVeAUVvAVZMwCINAQzzNxIxJzMVZkICxq4EZmIChrMEJjICxvn+BhkAABwjN5ICxqgEMqDSNxJFHBM3EgJG8/5GGQAhlP7oPdItAgHA/sAAACGS/sAgADgCIZH+ICMQ4CKC0D0gRYsBPQItDAG5/sAAACKj6AG2/sAAAMbj/lhdSE04PSItAsVqAQbg/gAyDQMiDQKAMxEgMyAyw/AizRhFSQHG2f4AAABSzRhSYSQiDQMyDQKAIhEwIiAiwvAiYSoMH4Z0BCF3/nGW/rIiAGEy/oKgAyInApIhKoJhJ7DGwCc5BAwaomEnsmE2hTkBsiE2cW3+UiEkYiEqcEvAykRqVQuEUmElgmEshwQCxk0Ed7sCRkwEmO2iLRBSLRUobZJhKKJhJlJhKTxTyH3iLRT4/SezAkbuAzFc/jAioCgCoAIAMUL+DA4MEumT6YMp0ymj4mEm/Q7iYSjNDkYGAHIhJwwTcGEEfMRgQ5NtBDliXQtyISQG4AMAgiEkkiElITP+l7jZMggAG3g5goYGAKIhJwwjMGoQfMUMFGBFg20EOWJdC0bUA3IhJFIhJSEo/le321IHAPiCWZKALxEc81oiQmExUmE0smE2G9eFeQEME0IhMVIhNLIhNlYSASKgICBVEFaFAPAgNCLC+CA1g/D0QYv/DBJhLv4AH0AAUqFXNg8AD0BA8JEMBvBigzBmIJxGDB8GAQAAANIhJCEM/ixDOWJdCwabAF0Ltjwehg4AciEnfMNwYQQMEmAjg20CDDOGFQBdC9IhJEYAAP0GgiElh73bG90LLSICAAAcQAAioYvMIO4gtjzkbQ9x+P3gICQptyAhQSnH4ONBwsz9VuIfwCAkJzwoRhEAkiEnfMOQYQQMEmAjg20CDFMh7P05Yn0NxpQDAAAAXQvSISRGAAD9BqIhJae90RvdCy0iAgAAHEAAIqGLzCDuIMAgJCc84cAgJAACQODgkSKv+CDMEPKgABacBoYMAAAAciEnfMNwYQQMEmAjg20CDGMG5//SISRdC4IhJYe94BvdCy0iAgAAHEAAIqEg7iCLzLaM5CHM/cLM+PoyIeP9KiPiQgDg6EGGDAAAAJIhJwwTkGEEfMRgNINtAwxzxtT/0iEkXQuiISUhv/2nvd1B1v0yDQD6IkoiMkIAG90b//ZPAobc/yHt/Xz28hIcIhIdIGYwYGD0Z58Hxh0A0iEkXQssc8Y/ALaMIAYPAHIhJ3zDcGEEDBJgI4NtAjwzBrz/AABdC9IhJEYAAP0GgiElh73ZG90LLSICAAAcQAAioYvMIO4gtozkbQ/gkHSSYSjg6EHCzPj9BkYCADxDhtQC0iEkXQsha/0nte+iISgLb6JFABtVFoYHVrz4hhwADJPGywJdC9IhJEYAAP0GIWH9J7XqhgYAciEnfMNwYQQMEmAjg20CLGPGmf8AANIhJF0LgiElh73ekVb90GjAUCnAZ7IBbQJnvwFtD00G0D0gUCUgUmE0YmE1smE2Abz9wAAAYiE1UiE0siE2at1qVWBvwFZm+UbQAv0GJjIIxgQAANIhJF0LDKMhb/05Yn0NBhcDAAAMDyYSAkYgACKhICJnESwEIYL9QmcSMqAFUmE0YmE1cmEzsmE2Aab9wAAAciEzsiE2YiE1UiE0PQcioJBCoAhCQ1gLIhszVlL/IqBwDJMyR+gLIht3VlL/HJRyoViRVf0MeEYCAAB6IpoigkIALQMbMkeT8SFq/TFq/QyEBgEAQkIAGyI3kvdGYQEhZ/36IiICACc8HUYPAAAAoiEnfMOgYQQMEmAjg20CDLMGVP/SISRdCyFc/foiYiElZ73bG90LPTIDAAAcQAAzoTDuIDICAIvMNzzhIVT9QVT9+iIyAgAMEgATQAAioUBPoAsi4CIQMMzAAANA4OCRSAQxLf0qJDA/oCJjERv/9j8Cht7/IUf9QqEgDANSYTSyYTYBaP3AAAB9DQwPUiE0siE2RhUAAACCISd8w4BhBAwSYCODbQIM4wa0AnIhJF0LkiEll7fgG3cLJyICAAAcQAAioSDuIIvMtjzkITP9QRL9+iIiAgDgMCQqRCEw/cLM/SokMkIA4ONBG/8hC/0yIhM3P9McMzJiE90HbQ8GHQEATAQyoAAiwURSYTRiYTWyYTZyYTMBQ/3AAAByITOB/fwioWCAh4JBHv0qKPoiDAMiwhiCYTIBO/3AAACCITIhGf1CpIAqKPoiDAMiwhgBNf3AAACoz4IhMvAqoCIiEYr/omEtImEuTQ9SITRiITVyITOyITbGAwAiD1gb/xAioDIiERszMmIRMiEuQC/ANzLmDAIpESkBrQIME+BDEZLBREr5mA9KQSop8CIRGzMpFJqqZrPlMeb8OiKMEvYqKyHW/EKm0EBHgoLIWCqIIqC8KiSCYSsMCXzzQmE5ImEwxkMAAF0L0iEkRgAA/QYsM8aZAACiISuCCgCCYTcWiA4QKKB4Ahv3+QL9CAwC8CIRImE4QiE4cCAEImEvC/9AIiBwcUFWX/4Mp4c3O3B4EZB3IAB3EXBwMUIhMHJhLwwacbb8ABhAAKqhKoRwiJDw+hFyo/+GAgAAQiEvqiJCWAD6iCe38gYgAHIhOSCAlIqHoqCwQan8qohAiJBymAzMZzJYDH0DMsP+IClBoaP88qSwxgoAIIAEgIfAQiE5fPeAhzCKhPCIgKCIkHKYDMx3MlgMMHMgMsP+giE3C4iCYTdCITcMuCAhQYeUyCAgBCB3wHz6IiE5cHowenIipLAqdyGO/CB3kJJXDEIhKxuZG0RCYStyIS6XFwLGvf+CIS0mKALGmQBGggAM4seyAsYwAJIhJdApwKYiAoYlACGj/OAwlEF9/CojQCKQIhIMADIRMCAxlvIAMCkxFjIFJzwCRiQAhhIAAAyjx7NEkZj8fPgAA0DgYJFgYAQgKDAqJpoiQCKQIpIMG3PWggYrYz0HZ7zdhgYAoiEnfMOgYQQMEmAjg20CHAPGdv4AANIhJF0LYiElZ73eIg0AGz0AHEAAIqEg7iCLzAzi3QPHMgLG2v8GCAAiDQEyzAgAE0AAMqEiDQDSzQIAHEAAIqEgIyAg7iDCzBAhdfzgMJRhT/wqI2AikDISDAAzETAgMZaiADA5MSAghEYJAAAAgWz8DKR89xs0AARA4ECRQEAEICcwKiSKImAikCKSDE0DliL+AANA4OCRMMzAImEoDPMnIxUhOvxyISj6MiFe/Bv/KiNyQgAGNAAAgiEoZrga3H8cCZJhKAYBANIhJF0LHBMhL/x89jliBkH+MVP8KiMiwvAiAgAiYSYnPB0GDgCiISd8w6BhBAwSYCODbQIcI8Y1/gAA0iEkXQtiISVnvd4b3QstIgIAciEmABxAACKhi8wg7iB3POGCISYxQPySISgMFgAYQABmoZozC2Yyw/DgJhBiAwAACEDg4JEqZiE5/IDMwCovDANmuQwxDPz6QzE1/Do0MgMATQZSYTRiYTWyYTYBSfzAAABiITVSITRq/7IhNoYAAAAMD3EB/EInEWInEmpkZ78Chnj/95YHhgIA0iEkXQscU0bJ/wDxIfwhIvw9D1JhNGJhNbJhNnJhMwE1/MAAAHIhMyEL/DInEUInEjo/ATD8wAAAsiE2YiE1UiE0Mer7KMMLIinD8ej7eM/WN7iGPgFiISUM4tA2wKZDDkG2+1A0wKYjAkZNAMYyAseyAoYuAKYjAkYlAEHc++AglEAikCISvAAyETAgMZYSATApMRZSBSc8AsYkAAYTAAAAAAyjx7NEfPiSpLAAA0DgYJFgYAQgKDAqJpoiQCKQIpIMG3PWggYrYz0HZ7zdhgYAciEnfMNwYQQMEmAjg20CHHPG1P0AANIhJF0LgiElh73eIg0AGz0AHEAAIqEg7iCLzAzi3QPHMgKG2/8GCAAAACINAYs8ABNAADKhIg0AK90AHEAAIqEgIyAg7iDCzBBBr/vgIJRAIpAiErwAIhEg8DGWjwAgKTHw8ITGCAAMo3z3YqSwGyMAA0DgMJEwMATw9zD682r/QP+Q8p8MPQKWL/4AAkDg4JEgzMAioP/3ogLGQACGAgAAHIMG0wDSISRdCyFp+ye17/JFAG0PG1VG6wAM4scyGTINASINAIAzESAjIAAcQAAioSDuICvdwswQMYr74CCUqiIwIpAiEgwAIhEgMDEgKTHWEwIMpBskAARA4ECRQEAEMDkwOjRBf/uKM0AzkDKTDE0ClvP9/QMAAkDg4JEgzMB3g3xioA7HNhpCDQEiDQCARBEgJCAAHEAAIqEg7iDSzQLCzBBBcPvgIJSqIkAikEISDABEEUAgMUBJMdYSAgymG0YABkDgYJFgYAQgKTAqJmFl+4oiYCKQIpIMbQSW8v0yRQAABEDg4JFAzMB3AggbVf0CRgIAAAAiRQErVQZz//BghGb2AoazACKu/ypmIYH74GYRaiIoAiJhJiF/+3IhJmpi+AYWhwV3PBzGDQCCISd8w4BhBAwSYCODbQIck4Zb/QDSISRdC5IhJZe93xvdCy0iAgCiISYAHEAAIqGLzCDuIKc84WIhJgwSABZAACKhCyLgIhBgzMAABkDg4JEq/wzix7IChjAAciEl0CfApiICxiUAQTP74CCUQCKQItIPIhIMADIRMCAxlgIBMCkxFkIFJzwChiQAxhIAAAAMo8ezRJFW+3z4AANA4GCRYGAEICgwKiaaIkAikCKSDBtz1oIGK2M9B2e83YYGAIIhJ3zDgGEEDBJgI4NtAhyjxiv9AADSISRdC5IhJZe93iINABs9ABxAACKhIO4gi8wM4t0DxzICBtv/BggAAAAiDQGLPAATQAAyoSINACvdABxAACKhICMgIO4gwswQYQb74CCUYCKQItIPMhIMADMRMCAxloIAMDkxICCExggAgSv7DKR89xs0AARA4ECRQEAEICcwKiSKImAikCKSDE0DliL+AANA4OCRMMzAMSH74CIRKjM4AzJhJjEf+6IhJiojKAIiYSgWCganPB5GDgByISd8w3BhBAwSYCODbQIcs8b3/AAAANIhJF0LgiElh73dG90LLSICAJIhJgAcQAAioYvMIO4glzzhoiEmDBIAGkAAIqFiISgLIuAiECpmAApA4OCRoMzAYmEocen6giEocHXAkiEsMeb6gCfAkCIQOiJyYSk9BSe1AT0CQZ36+jNtDze0bQYSACHH+ixTOWLGbQA8UyHE+n0NOWIMJgZsAF0L0iEkRgAA/QYhkvonteGiISliIShyISxgKsAx0PpwIhAqIyICABuqIkUAomEpG1ULb1Yf/QYMAAAyAgBixv0yRQAyAgEyRQEyAgI7IjJFAjtV9jbjFgYBMgIAMkUAZiYFIgIBIkUBalX9BqKgsHz5gqSwcqEABr3+IaP6KLIH4gIGl/zAICQnPCBGDwCCISd8w4BhBAwSYCODbQIsAwas/AAAXQvSISRGAAD9BpIhJZe92RvdCy0iAgAAHEAAIqGLzCDuIMAgJCc84cAgJAACQODgkXyCIMwQfQ1GAQAAC3fCzPiiISR3ugL2jPEht/oxt/pNDFJhNHJhM7JhNoWVAAsisiE2ciEzUiE0IO4QDA8WLAaGDAAAAIIhJ3zDgGEEDBJgI4NtAiyTBg8AciEkXQuSISWXt+AbdwsnIgIAABxAACKhIO4gi8y2jOTgMHTCzPjg6EEGCgCiISd8w6BhBAwSYCODbQIsoyFm+jliRg8AciEkXQtiISVnt9syBwAbd0Fg+hv/KKSAIhEwIiAppPZPCEbe/wByISRdCyFa+iwjOWIMBoYBAHIhJF0LfPYmFhVLJsxyhgMAAAt3wsz4giEkd7gC9ozxgU/6IX/6MX/6yXhNDFJhNGJhNXJhM4JhMrJhNgWHAIIhMpIhKKIhJgsimeiSISng4hCiaBByITOiISRSITSyITZiITX5+OJoFJJoFaDXwLDFwP0GllYOMWz6+NgtDEV/APDg9E0C8PD1fQwMeGIhNbIhNkYlAAAAkgIAogIC6umSAgHqmZru+v7iAgOampr/mp7iAgSa/5qe4gIFmv+anuICBpr/mp7iAgea/5ru6v+LIjqSRznAQCNBsCKwsJBgRgIAADICABsiOu7q/yo5vQJHM+8xTvotDkJhMWJhNXJhM4JhMrJhNoV2ADFI+u0CLQ8FdgBCITFyITOyITZAd8CCITJBQfpiITX9AoyHLQuwOMDG5v8AAAD/ESEI+urv6dL9BtxW+KLw7sB87+D3g0YCAAAAAAwM3Qzyr/0xNPpSISooI2IhJNAiwNBVwNpm0RD6KSM4DXEP+lJhKspTWQ1wNcAMAgwV8CWDYmEkICB0VoIAQtOAQCWDFpIAwQX6LQwFKgDJDYIhKtHs+Yz4KD0WsgDwLzHwIsDWIgDGhPvWjwAioMcpXQY6AABWTw4oPcwSRlH6IqDIhgAAIqDJKV3GTfooLYwSBkz6Ie75ARv6wAAAAR76wAAAhkf6yD3MHMZF+iKj6AEV+sAAAMAMAAZC+gDiYSIMfEaU+gEV+sAAAAwcDAMGCAAAyC34PfAsICAgtMwSxpv6Ri77Mi0DIi0ChTMAMqAADBwgw4PGKft4fWhtWF1ITTg9KC0MDAH7+cAAAO0CDBLgwpOGJfsAAAH1+cAAAAwMBh/7ACHI+UhdOC1JAiHG+TkCBvr/QcT5DAI4BMKgyDDCgykEQcD5PQwMHCkEMMKDBhP7xzICxvP9xvr9KD0WIvLGF/oCIUOSoRDCIULSIUHiIUDyIT+aEQ3wAAAIAABgHAAAYAAAAGAQAABgIfz/EsHw6QHAIADoAgkxySHZESH4/8AgAMgCwMB0nOzRmvlGBAAAADH0/8AgACgDOA0gIHTAAwALzGYM6ob0/yHv/wgxwCAA6QLIIdgR6AESwRAN8AAAAPgCAGAQAgBgAAIAYAAAAAgh/P/AIAA4AjAwJFZD/yH5/0H6/8AgADkCMff/wCAASQPAIABIA1Z0/8AgACgCDBMgIAQwIjAN8AAAgAAAAABA////AAQCAGASwfDJIcFw+QkxKEzZERbiCEX6/xaCCChMDPMMDSejDCgsMCIQDBMg04PQ0HQQESBF+P8WYv8h3v8x7v/AIAA5AsAgADgCVnP/Mdf/wCAAKAMgICRWQv8oLDHn/0AiESezFhwDDBLQI5M4TCAzwDlMOCwqIykshgkAQd3/MV750DSTQd7/wCAAImQAIcn/wCAAMmIAwCAAOAJWc/+G8P8ACDHIIdgREsEQDfAATEoAQBLB4MlhwUT5+TH4POlBCXHZUe0C97MB/QMWHwTYHNrf0NxBBgEAAABF8v8oTKYSBCgsJ63yBe3/FpL/KBxNDz0OAe7/wAAAICB0jDIioMQpXCgcSDz6IvBEwCkcSTwIcchh2FHoQfgxEsEgDfAAAAD/DwAAUSn5EsHwCTEMFEJFADBMQUklQfr/ORUpNTAwtEoiKiMgLEEpRQwCImUFAVv5wAAACDEyoMUgI5MSwRAN8AAAADA7AEASwfAJMTKgwDeSESKg2wH7/8AAACKg3EYEAAAAADKg2zeSCAH2/8AAACKg3QH0/8AAAAgxEsEQDfAAAAASwfDJIdkRCTHNAjrSRgIAACIMAMLMAcX6/9ec8wIhA8IhAtgREsEQDfAAAFgQAABwEAAAGJgAQBxLAEA0mABAAJkAQJH7/xLB4Mlh6UH5MQlx2VGQEcDtAiLREM0DAfX/wAAA8fn4hgoA3QzHvwHdD00NPQEtDgHw/8AAACAgdPxCTQ09ASLREAHs/8AAANDugNDMwFYc/SHl/zLREBAigAHn/8AAACHh/xwDGiIF9f8tDAYBAAAAIqBjkd3/mhEIcchh2FHoQfgxEsEgDfAAEsHwIqDACTEBuv/AAAAIMRLBEA3wAAAAbBAAAGgQAAB0EAAAeBAAAHwQAACAEAAAkBAAAJgPAECMOwBAEsHgkfz/+TH9AiHG/8lh2VEJcelBkBHAGiI5AjHy/ywCGjNJA0Hw/9LREBpEwqAAUmQAwm0aAfD/wAAAYer/Ib/4GmZoBmeyAsZJAC0NAbb/wAAAIbP/MeX/KkEaM0kDRj4AAABhr/8x3/8aZmgGGjPoA8AmwOeyAiDiIGHd/z0BGmZZBk0O8C8gAaj/wAAAMdj/ICB0GjNYA4yyDARCbRbtBMYSAAAAAEHR/+r/GkRZBAXx/z0OLQGF4/9F8P9NDj0B0C0gAZr/wAAAYcn/6swaZlgGIZP/GiIoAie8vDHC/1AswBozOAM3sgJG3f9G6v9CoABCTWwhuf8QIoABv//AAABWAv9huf8iDWwQZoA4BkUHAPfiEfZODkGx/xpE6jQiQwAb7sbx/zKv/jeSwSZOKSF7/9A9IBAigAF+/8AAAAXo/yF2/xwDGiJF2v9F5/8sAgGq+MAAAIYFAGFx/1ItGhpmaAZntchXPAIG2f/G7/8AkaD/mhEIcchh2FHoQfgxEsEgDfBdAkKgwCgDR5UOzDIMEgYHAAwCKQN84g3wJhIHJiIUxgwAAABCoNstBUeVKwwiKQOGCAAAIqDcJ5UJDBIpAy0EDfAAAEKg3XzyR5ULDBIpAyKg2w3wAHzyDfAAALYjMG0CUPZAQPNAR7UpUETAABRAADOhDAI3NgQwZsAbIvAiETAxQQtEVsT+NzYBGyIN8ACMkw3wNzYMDBIN8AAAAAAARElWMAwCDfC2IyhQ8kBA80BHtRdQRMAAFEAAM6E3MgIwIsAwMUFCxP9WBP83MgIwIsAN8MxTAAAARElWMAwCDfAAAAAAFEDmxAkgM4EAIqEN8AAAADKhDAIN8AA=", "text_start": 1074843648, "data_start": 1073720488} -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # basic testing script for local debugging 3 | # note you need https to even think of doing 4 | # web serial or web usb so make sure this is 5 | # setup and running... 6 | 7 | # nothing fancy just http-server (node) and 8 | # mkcert to generate the certs. 9 | 10 | # easily available via apt and brew 11 | # you probably want to run via screen 12 | 13 | if [ ! -f "./localhost.pem" ]; then 14 | mkcert localhost 15 | fi 16 | http-server -S -C localhost.pem -K localhost-key.pem -g -c -t10 17 | echo done 18 | 19 | --------------------------------------------------------------------------------