From d9ad884d48b9cc389f839b65a0c79ed7e144a410 Mon Sep 17 00:00:00 2001 From: Luc <8822552+luc-github@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:31:57 +0800 Subject: [PATCH] Merge 3.0 refactoring ## What's Changed * Complete refactoring using spectre.css and correct Preact API per @alexblog suggestion * Smaller footprint * Add support for extensions/pluggins * Add theme support by using external css * Remove banner as unnecessary * Remove information bar and put information to separate page and respective panel * Add audio and haptic feedback * Add Repetier / ESP3DLib V2 / Smoothieware / GRBL support * Split code for 3DPrinter / CNC / Sand Table ## Limitations * CNC / Sand Table specific UI is still not defined * Wizard is not implemented yet * Works with ESP3D 3.0- alpha-2 - no backward compatibility * Language packs are not ready to be translated yet as UI is not finished ## Discussion / status [Discussion](https://github.com/luc-github/ESP3D-WEBUI/discussions/94) ## New Contributors @alexblog made several contributions like https://github.com/luc-github/ESP3D-WEBUI/pull/236 --- .babelrc | 6 - .gitignore | 9 +- .nvmrc | 1 + .prettierrc | 2 +- .travis.yml | 17 - ESP3D-WebUI-features.xls | Bin 13824 -> 13824 bytes Features.md | 73 + Memo/ESP3D [ESP400] format.txt | 92 + Memo/TargetFW.txt | 7 + Memo/data structure.odt | Bin 0 -> 18339 bytes Memo/realtimecmd.txt | 47 + Notes.txt | 16 + README.md | 63 +- config/pack.js | 82 + config/server.js | 322 + config/targets/CNC/FluidNC/index.js | 669 + config/targets/CNC/GRBL/index.js | 572 + .../Printer3D/Marlin-embedded/index.js | 843 + config/targets/Printer3D/Marlin/index.js | 845 + .../Printer3D/RepRap-embedded/index.js | 649 + config/targets/Printer3D/RepRap/index.js | 649 + config/targets/Printer3D/Repetier/index.js | 867 + .../targets/Printer3D/Smoothieware/index.js | 1125 + config/targets/SandTable/FluidNC/index.js | 671 + .../targets/SandTable/GRBL/index.js | 530 +- config/targets/SandTable/Marlin/index.js | 621 + config/webpack.dev.js | 87 + config/webpack.prod.js | 108 + dist/CNC/FluidNC/index.html.gz | Bin 0 -> 79821 bytes dist/CNC/GRBL/index.html.gz | Bin 0 -> 66398 bytes dist/Printer3D/Marlin-embedded/index.html.gz | Bin 0 -> 81766 bytes dist/Printer3D/Marlin/index.html.gz | Bin 0 -> 80970 bytes dist/Printer3D/RepRap-embedded/index.html.gz | Bin 0 -> 77260 bytes dist/Printer3D/RepRap/index.html.gz | Bin 0 -> 77241 bytes dist/Printer3D/Repetier/index.html.gz | Bin 0 -> 81016 bytes dist/Printer3D/Smoothieware/index.html.gz | Bin 0 -> 80892 bytes dist/SandTable/FluidNC/index.html.gz | Bin 0 -> 79817 bytes dist/SandTable/GRBL/index.html.gz | Bin 0 -> 66388 bytes dist/grbl/debug/index.html.gz | Bin 66883 -> 0 bytes dist/grbl/languages/fr.json | 30 - dist/grbl/languages/fr.json.gz | Bin 611 -> 0 bytes dist/grbl/languages/zh_cn.json | 4 - dist/grbl/languages/zh_cn.json.gz | Bin 76 -> 0 bytes dist/grbl/production/index.html.gz | Bin 65638 -> 0 bytes dist/printer/debug/index.html.gz | Bin 99486 -> 0 bytes dist/printer/languages/fr.json | 30 - dist/printer/languages/fr.json.gz | Bin 541 -> 0 bytes dist/printer/languages/zh_cn.json | 4 - dist/printer/languages/zh_cn.json.gz | Bin 76 -> 0 bytes dist/printer/production/index.html.gz | Bin 97809 -> 0 bytes esp3d.todo | 3 + extensions_samples/API.md | 646 + extensions_samples/capabilities.html | 30 + extensions_samples/dispatch.html | 28 + extensions_samples/download.html | 60 + extensions_samples/modal.html | 32 + extensions_samples/query.html | 33 + extensions_samples/sound.html | 14 + extensions_samples/terminal.html | 49 + extensions_samples/toast.html | 14 + extensions_samples/translate.html | 34 + extensions_samples/upload.html | 45 + nodemon.json | 4 - package-lock.json | 25058 ++++++++++------ package.json | 128 +- postcss.config.js | 12 - prettyfiles.bat | 5 - server/CNC/FluidNC/Flash/config.yaml | 227 + server/Printer3D/Marlin/Flash/index.html.gz | Bin 0 -> 79176 bytes .../Printer3D/Marlin/Flash/preferences.json | 112 + server/Printer3D/Marlin/Flash/theme-dark.gz | Bin 0 -> 272 bytes .../Printer3D/RepRap/Flash/preferences.json | 75 + server/public/capabilities.html | 30 + server/public/config.yaml | 98 + server/public/config.yml | 230 + server/public/dispatch.html | 28 + server/public/download.html | 60 + server/public/download.html.gz | Bin 0 -> 578 bytes {src/server => server}/public/favicon.ico | Bin server/public/image.jpg | Bin 0 -> 282686 bytes server/public/index.html | 1 + server/public/index.html.gz | Bin 0 -> 38371 bytes .../fr.json => server/public/lang-fr.json | 0 server/public/lang-zh_cn.json.gz | Bin 0 -> 87 bytes server/public/modal.html | 32 + server/public/my config.yaml | 96 + server/public/preferences.json | 59 + server/public/query.html | 33 + server/public/sound.html | 14 + server/public/terminal.html | 49 + server/public/theme-dark.gz | Bin 0 -> 272 bytes server/public/theme-purple | 41 + server/public/toast.html | 14 + server/public/translate.html | 34 + server/public/upload.html | 45 + {src/languages => server/public}/zh_cn.json | 0 sizetracker.txt | 56 +- src/adapters/httpAdapter.js | 120 + src/adapters/index.js | 24 + src/areas/connection.js | 239 + src/areas/footer.js | 27 + src/areas/index.js | 427 + .../banner.js => areas/informations.js} | 26 +- src/areas/main.js | 107 + src/areas/menu.js | 31 + src/components/App/index.js | 59 + src/components/{ => App}/version.js | 13 +- src/components/Controls/Button.js | 53 + src/components/Controls/ButtonImg.js | 68 + src/components/Controls/CenterLeft.js | 45 + src/components/Controls/Field.js | 81 + src/components/Controls/FieldGroup.js | 44 + src/components/Controls/Fields/Boolean.js | 131 + src/components/Controls/Fields/FormGroup.js | 78 + src/components/Controls/Fields/IconSelect.js | 112 + src/components/Controls/Fields/Input.js | 306 + src/components/Controls/Fields/ItemsList.js | 323 + src/components/Controls/Fields/Label.js | 31 + src/components/Controls/Fields/PickUp.js | 104 + src/components/Controls/Fields/Select.js | 78 + src/components/Controls/Fields/def_macro.json | 7 + src/components/Controls/Fields/def_panel.json | 10 + src/components/Controls/Fields/index.js | 40 + src/components/Controls/Loading.js | 31 + src/components/Controls/Modal.js | 38 + src/components/Controls/Progress.js | 62 + src/components/Controls/ScanAp.js | 113 + src/components/Controls/ScanPacksList.js | 158 + src/components/Controls/Toast.js | 31 + src/components/Controls/index.js | 44 + src/components/ExtraContent/index.js | 352 + src/components/Helpers/arrays.js | 86 + src/components/Helpers/components.js | 78 + src/components/Helpers/filters.js | 87 + src/components/Helpers/html.js | 29 + src/components/Helpers/http.js | 71 + src/components/Helpers/index.js | 73 + src/components/Helpers/strings.js | 98 + src/components/Helpers/time.js | 35 + src/components/Modal/confirmModal.js | 41 + src/components/Modal/genericModal.js | 84 + src/components/Modal/index.js | 99 + src/components/Modal/keepConnectedModal.js | 80 + src/components/Modal/logginModal.js | 143 + src/components/Modal/progressModal.js | 53 + src/components/Navbar/index.js | 318 + src/components/Panels/Charts.js | 487 + src/components/Panels/ExtraControls.js | 366 + src/components/Panels/ExtraPanel.js | 61 + src/components/Panels/Extruders.js | 299 + src/components/Panels/Files.js | 924 + src/components/Panels/Jog.js | 1662 + src/components/Panels/JogCNC.js | 1184 + src/components/Panels/Macros.js | 181 + src/components/Panels/Notifications.js | 199 + src/components/Panels/Status.js | 234 + src/components/Panels/Temperatures.js | 390 + src/components/Panels/Terminal.js | 300 + src/components/Panels/index.js | 49 + src/components/Router/index.js | 157 + src/components/TabBar/index.js | 87 + src/components/Toast/index.js | 75 + src/components/about/index.js | 408 - src/components/app.js | 226 - src/components/audio/index.js | 106 - src/components/dashboard/extrapanels.js | 276 - src/components/dashboard/index.js | 141 - src/components/dashboard/macros.js | 154 - src/components/dashboard/terminal.js | 444 - src/components/dialog/index.js | 450 - src/components/grbl/files.js | 91 - src/components/grbl/index.js | 861 - src/components/grbl/jog.js | 720 - src/components/grbl/preferences.js | 375 - src/components/grbl/store/axis.js | 9 - src/components/grbl/store/index.js | 22 - src/components/grbl/store/positions.js | 31 - src/components/header/index.js | 361 - src/components/http/index.js | 399 - src/components/images/icons.js | 239 + src/components/images/index.js | 16 +- src/components/images/logo.js | 92 +- src/components/notification/index.js | 85 - src/components/printer/en.json | 68 - src/components/printer/extrusion.js | 679 - src/components/printer/fan.js | 407 - src/components/printer/feedrate.js | 404 - src/components/printer/files.js | 1643 - src/components/printer/flowrate.js | 402 - src/components/printer/index.js | 1508 - src/components/printer/jog.js | 1592 - src/components/printer/notifications.js | 527 - src/components/printer/preferences.js | 769 - src/components/printer/sensors.js | 352 - src/components/printer/smoothie.js | 1329 - src/components/printer/status.js | 161 - src/components/printer/store/display.js | 48 - src/components/printer/store/fan.js | 9 - src/components/printer/store/feedrate.js | 9 - src/components/printer/store/flowrate.js | 9 - src/components/printer/store/index.js | 35 - src/components/printer/store/positions.js | 19 - src/components/printer/store/sensor.js | 28 - src/components/printer/store/status.js | 14 - src/components/printer/store/temperatures.js | 64 - src/components/printer/temperatures.js | 1193 - src/components/settings/esp3d.js | 874 - src/components/settings/index.js | 75 - src/components/settings/selector.js | 134 - src/components/settings/webui.js | 1992 -- src/components/settings/wizard.js | 265 - src/components/store/dialogcontent.js | 8 - src/components/store/display.js | 68 - src/components/store/error.js | 13 - src/components/store/filespanel.js | 12 - src/components/store/monitor.js | 9 - src/components/store/notificationsbar.js | 9 - src/components/translations/en.json | 292 - src/components/translations/index.js | 98 +- src/components/translations/languages.json | 5 + src/components/websocket/index.js | 308 - src/contexts/DatasContext.js | 78 + src/contexts/HttpQueueContext.js | 149 + src/contexts/RouterContext.js | 48 + src/contexts/SettingsContext.js | 76 + src/contexts/UiContext.js | 365 + src/contexts/WsContext.js | 231 + src/contexts/index.js | 50 + src/hooks/index.js | 24 + src/hooks/useHttpQueue.js | 138 + src/hooks/useSettings.js | 335 + src/index.html | 18 +- src/index.js | 17 +- src/languages/build.js | 79 - src/languages/grbl/fr.json | 4 - src/languages/language-list.json | 5 - src/languages/printer/fr.json | 4 - src/pages/about/index.js | 407 + src/pages/dashboard/index.js | 180 + src/pages/extrapages/index.js | 36 + src/pages/settings/index.js | 53 + src/server/index.js | 1486 - src/server/public/fr.json | 28 - src/server/public/hello.html | 5 - src/server/public/index.html.gz | Bin 71786 -> 0 bytes src/server/public/myfolder/favicon.ico | Bin 1150 -> 0 bytes src/server/public/myfolder/index.html.gz | Bin 60270 -> 0 bytes src/server/public/oem.json.tpl | 6 - src/server/public/preferences.json | 82 - src/style/_mixins.scss | 10 + src/style/_spectre-exp.scss | 14 + src/style/_spectre-icons.scss | 6 + src/style/_spectre.scss | 45 + src/style/_variables.scss | 127 + src/style/components/_app.scss | 95 + src/style/components/_control.scss | 476 + src/style/components/_media.scss | 17 + src/style/components/_menu.scss | 51 + src/style/components/_navbar.scss | 26 + src/style/components/_panel.scss | 56 + src/style/index.scss | 16 + src/stylesheets/_bootstrap.scss | 34 - src/stylesheets/_functions.scss | 14 - src/stylesheets/_vars.scss | 64 - src/stylesheets/application.scss | 20 - src/stylesheets/components/_banner.scss | 49 - src/stylesheets/components/_controls.scss | 487 - src/stylesheets/components/_modaldlg.scss | 101 - src/stylesheets/components/_navbar.scss | 63 - src/stylesheets/components/_notification.scss | 13 - src/stylesheets/components/grbl/_grbl.scss | 16 - .../components/printer/_printer.scss | 185 - src/tabs/features/exportHelper.js | 62 + src/tabs/features/formatHelper.js | 93 + src/tabs/features/importHelper.js | 69 + src/tabs/features/index.js | 615 + src/tabs/interface/exportHelper.js | 92 + src/tabs/interface/importHelper.js | 191 + src/tabs/interface/index.js | 523 + src/tabs/machine/index.js | 28 + src/targets/CNC/Controls/EmergencyButton.js | 70 + src/targets/CNC/Controls/index.js | 23 + src/targets/CNC/FluidNC/CMD-source.js | 73 + .../FluidNC/Controls/InformationsControls.js | 32 + .../CNC/FluidNC/Controls/QuickButtonsBar.js | 31 + src/targets/CNC/FluidNC/Controls/index.js | 24 + src/targets/CNC/FluidNC/DIRECTSD-source.js | 113 + src/targets/CNC/FluidNC/MachineSettings.js | 606 + src/targets/CNC/FluidNC/TargetContext.js | 220 + src/targets/CNC/FluidNC/exportHelper.js | 64 + src/targets/CNC/FluidNC/files.js | 98 + src/targets/CNC/FluidNC/icons.js | 86 + src/targets/CNC/FluidNC/importHelper.js | 147 + src/targets/CNC/FluidNC/index.js | 63 + src/targets/CNC/FluidNC/logo.js | 121 + src/targets/CNC/FluidNC/panels.js | 35 + src/targets/CNC/FluidNC/preferences.json | 48 + src/targets/CNC/FluidNC/processor.js | 138 + src/targets/CNC/FluidNC/realCommandsTable.js | 42 + src/targets/CNC/FluidNC/stream.js | 35 + src/targets/CNC/FluidNC/style/index.scss | 12 + src/targets/CNC/FluidNC/translations/en.json | 67 + src/targets/CNC/GRBL/CMD-source.js | 89 + .../CNC/GRBL/Controls/InformationsControls.js | 32 + .../CNC/GRBL/Controls/QuickButtonsBar.js | 31 + src/targets/CNC/GRBL/Controls/index.js | 24 + src/targets/CNC/GRBL/MachineSettings.js | 261 + src/targets/CNC/GRBL/TargetContext.js | 220 + src/targets/CNC/GRBL/files.js | 84 + src/targets/CNC/GRBL/icons.js | 86 + src/targets/CNC/GRBL/index.js | 63 + src/targets/CNC/GRBL/panels.js | 35 + src/targets/CNC/GRBL/preferences.json | 21 + src/targets/CNC/GRBL/processor.js | 138 + src/targets/CNC/GRBL/realCommandsTable.js | 42 + src/targets/CNC/GRBL/stream.js | 35 + src/targets/CNC/GRBL/style/index.scss | 1 + .../CNC/GRBL/translations}/en.json | 23 +- src/targets/CNC/preferences.json | 178 + src/targets/CNC/style/_index.scss | 81 + src/targets/CNC/translations/en.json | 36 + src/targets/FLASH-source.js | 104 + .../Printer3D/Controls/EmergencyButton.js | 66 + src/targets/Printer3D/Controls/index.js | 23 + .../Printer3D/Marlin-embedded/CMD-source.js | 116 + .../Controls/InformationsControls.js | 40 + .../Controls/MixedExtrudersControl.js | 378 + .../Controls/QuickButtonsBar.js | 31 + .../Marlin-embedded/Controls/index.js | 25 + .../Marlin-embedded/DIRECTSD-source.js | 113 + .../Marlin-embedded/MachineSettings.js | 355 + .../Printer3D/Marlin-embedded/SD-source.js | 127 + .../Marlin-embedded/TFT-SD-source.js | 224 + .../Marlin-embedded/TFT-USB-source.js | 224 + .../Marlin-embedded/TargetContext.js | 323 + .../Printer3D/Marlin-embedded/files.js | 114 + .../Printer3D/Marlin-embedded/filters.js | 256 + .../Printer3D/Marlin-embedded/icons.js} | 52 +- .../Printer3D/Marlin-embedded/index.js | 66 + src/targets/Printer3D/Marlin-embedded/logo.js | 95 + .../Printer3D/Marlin-embedded/panels.js | 45 + .../Marlin-embedded/preferences.json | 172 + .../Printer3D/Marlin-embedded/processor.js | 144 + .../Printer3D/Marlin-embedded/stream.js | 40 + .../Marlin-embedded/style/index.scss | 46 + .../Marlin-embedded/translations/en.json | 3 + src/targets/Printer3D/Marlin/CMD-source.js | 116 + .../Marlin/Controls/InformationsControls.js | 40 + .../Marlin/Controls/MixedExtrudersControl.js | 378 + .../Marlin/Controls/QuickButtonsBar.js | 31 + .../Printer3D/Marlin/Controls/index.js | 25 + .../Printer3D/Marlin/DIRECTSD-source.js | 113 + .../Printer3D/Marlin/MachineSettings.js | 355 + src/targets/Printer3D/Marlin/SD-source.js | 158 + src/targets/Printer3D/Marlin/TFT-SD-source.js | 224 + .../Printer3D/Marlin/TFT-USB-source.js | 224 + src/targets/Printer3D/Marlin/TargetContext.js | 323 + src/targets/Printer3D/Marlin/files.js | 114 + src/targets/Printer3D/Marlin/filters.js | 259 + src/targets/Printer3D/Marlin/icons.js | 110 + src/targets/Printer3D/Marlin/index.js | 66 + src/targets/Printer3D/Marlin/panels.js | 45 + src/targets/Printer3D/Marlin/preferences.json | 182 + src/targets/Printer3D/Marlin/processor.js | 144 + src/targets/Printer3D/Marlin/stream.js | 40 + src/targets/Printer3D/Marlin/style/index.scss | 46 + .../Printer3D/Marlin/translations/en.json | 3 + .../Printer3D/RepRap-embedded/CMD-source.js | 116 + .../Controls/InformationsControls.js | 36 + .../Controls/MixedExtrudersControl.js} | 23 +- .../Controls/QuickButtonsBar.js | 31 + .../RepRap-embedded/Controls/index.js | 25 + .../RepRap-embedded/DIRECTSD-source.js | 113 + .../RepRap-embedded/MachineSettings.js | 356 + .../Printer3D/RepRap-embedded/SD-source.js | 124 + .../RepRap-embedded/TargetContext.js | 215 + .../Printer3D/RepRap-embedded/files.js | 112 + .../Printer3D/RepRap-embedded/filters.js | 80 + .../Printer3D/RepRap-embedded/icons.js | 110 + .../Printer3D/RepRap-embedded/index.js | 66 + .../Printer3D/RepRap-embedded/panels.js | 41 + .../RepRap-embedded/preferences.json | 70 + .../Printer3D/RepRap-embedded/processor.js | 140 + .../Printer3D/RepRap-embedded/stream.js | 38 + .../RepRap-embedded/style/index.scss | 46 + .../RepRap-embedded/translations/en.json | 3 + src/targets/Printer3D/RepRap/CMD-source.js | 116 + .../RepRap/Controls/InformationsControls.js | 36 + .../RepRap/Controls/MixedExtrudersControl.js | 29 + .../RepRap/Controls/QuickButtonsBar.js | 31 + .../Printer3D/RepRap/Controls/index.js | 25 + .../Printer3D/RepRap/DIRECTSD-source.js | 113 + .../Printer3D/RepRap/MachineSettings.js | 357 + src/targets/Printer3D/RepRap/SD-source.js | 124 + src/targets/Printer3D/RepRap/TargetContext.js | 214 + src/targets/Printer3D/RepRap/files.js | 112 + src/targets/Printer3D/RepRap/filters.js | 80 + src/targets/Printer3D/RepRap/icons.js | 110 + src/targets/Printer3D/RepRap/index.js | 66 + src/targets/Printer3D/RepRap/panels.js | 41 + src/targets/Printer3D/RepRap/preferences.json | 70 + src/targets/Printer3D/RepRap/processor.js | 140 + src/targets/Printer3D/RepRap/stream.js | 38 + src/targets/Printer3D/RepRap/style/index.scss | 46 + .../Printer3D/RepRap/translations/en.json | 3 + src/targets/Printer3D/Repetier/CMD-source.js | 128 + .../Repetier/Controls/InformationsControls.js | 40 + .../Controls/MixedExtrudersControl.js | 380 + .../Repetier/Controls/QuickButtonsBar.js | 31 + .../Printer3D/Repetier/Controls/index.js | 25 + .../Printer3D/Repetier/DIRECTSD-source.js | 113 + .../Printer3D/Repetier/MachineSettings.js | 354 + src/targets/Printer3D/Repetier/SD-source.js | 151 + .../Printer3D/Repetier/TFT-SD-source.js | 224 + .../Printer3D/Repetier/TFT-USB-source.js | 224 + .../Printer3D/Repetier/TargetContext.js | 329 + src/targets/Printer3D/Repetier/files.js | 114 + src/targets/Printer3D/Repetier/filters.js | 300 + src/targets/Printer3D/Repetier/icons.js | 110 + src/targets/Printer3D/Repetier/index.js | 66 + src/targets/Printer3D/Repetier/panels.js | 45 + .../Printer3D/Repetier/preferences.json | 202 + src/targets/Printer3D/Repetier/processor.js | 149 + src/targets/Printer3D/Repetier/stream.js | 40 + .../Printer3D/Repetier/style/index.scss | 46 + .../Printer3D/Repetier/translations/en.json | 3 + .../Printer3D/Smoothieware/CMD-source.js | 175 + .../Controls/InformationsControls.js | 40 + .../Controls/MixedExtrudersControl.js | 29 + .../Smoothieware/Controls/QuickButtonsBar.js | 31 + .../Printer3D/Smoothieware/Controls/index.js | 25 + .../Printer3D/Smoothieware/DIRECTSD-source.js | 113 + .../Smoothieware/DIRECTSDEXT-source.js | 113 + .../Printer3D/Smoothieware/MachineSettings.js | 595 + .../Printer3D/Smoothieware/SD-source.js | 154 + .../Printer3D/Smoothieware/SDEXT-source.js | 129 + .../Printer3D/Smoothieware/TFT-SD-source.js | 224 + .../Printer3D/Smoothieware/TFT-USB-source.js | 224 + .../Printer3D/Smoothieware/TargetContext.js | 321 + src/targets/Printer3D/Smoothieware/files.js | 138 + src/targets/Printer3D/Smoothieware/filters.js | 259 + src/targets/Printer3D/Smoothieware/icons.js | 110 + src/targets/Printer3D/Smoothieware/index.js | 66 + src/targets/Printer3D/Smoothieware/panels.js | 45 + .../Printer3D/Smoothieware/preferences.json | 216 + .../Printer3D/Smoothieware/processor.js | 146 + src/targets/Printer3D/Smoothieware/stream.js | 38 + .../Printer3D/Smoothieware/style/index.scss | 1 + .../Smoothieware/translations/en.json | 7 + src/targets/Printer3D/icon.js | 99 + src/targets/Printer3D/preferences.json | 436 + src/targets/Printer3D/style/_index.scss | 315 + src/targets/Printer3D/translations/en.json | 106 + .../SandTable/Controls/EmergencyButton.js | 71 + src/targets/SandTable/Controls/index.js | 23 + src/targets/SandTable/FluidNC/CMD-source.js | 73 + .../FluidNC/Controls/InformationsControls.js | 32 + .../FluidNC/Controls/QuickButtonsBar.js | 31 + .../SandTable/FluidNC/Controls/index.js | 24 + .../SandTable/FluidNC/DIRECTSD-source.js | 113 + .../SandTable/FluidNC/MachineSettings.js | 606 + .../SandTable/FluidNC/TargetContext.js | 220 + src/targets/SandTable/FluidNC/exportHelper.js | 64 + src/targets/SandTable/FluidNC/files.js | 98 + src/targets/SandTable/FluidNC/icons.js | 86 + src/targets/SandTable/FluidNC/importHelper.js | 147 + src/targets/SandTable/FluidNC/index.js | 63 + src/targets/SandTable/FluidNC/logo.js | 121 + src/targets/SandTable/FluidNC/panels.js | 35 + .../SandTable/FluidNC/preferences.json | 48 + src/targets/SandTable/FluidNC/processor.js | 138 + .../SandTable/FluidNC/realCommandsTable.js | 42 + src/targets/SandTable/FluidNC/stream.js | 35 + .../SandTable/FluidNC/style/index.scss | 12 + .../SandTable/FluidNC/translations/en.json | 100 + src/targets/SandTable/GRBL/CMD-source.js | 89 + .../GRBL/Controls/InformationsControls.js | 32 + .../GRBL/Controls/QuickButtonsBar.js | 31 + src/targets/SandTable/GRBL/Controls/index.js | 24 + src/targets/SandTable/GRBL/MachineSettings.js | 261 + src/targets/SandTable/GRBL/TargetContext.js | 220 + src/targets/SandTable/GRBL/files.js | 84 + src/targets/SandTable/GRBL/icons.js | 86 + src/targets/SandTable/GRBL/index.js | 63 + src/targets/SandTable/GRBL/panels.js | 35 + src/targets/SandTable/GRBL/preferences.json | 21 + src/targets/SandTable/GRBL/processor.js | 138 + .../SandTable/GRBL/realCommandsTable.js | 42 + src/targets/SandTable/GRBL/stream.js | 35 + src/targets/SandTable/GRBL/style/index.scss | 1 + .../SandTable/GRBL/translations/en.json | 88 + src/targets/SandTable/preferences.json | 179 + src/targets/SandTable/style/_index.scss | 81 + src/targets/SandTable/translations/en.json | 4 + src/targets/helpers.js | 37 + src/targets/index.js | 78 + src/targets/preferences.json | 202 + src/targets/translations/en.json | 331 + themes_samples/theme-dark.gz | Bin 0 -> 272 bytes themes_samples/theme-purple | 41 + webpack.config.js | 7 - webpack/development.js | 18 - webpack/environment.js | 35 - webpack/plugins/compression-plugin.js | 15 - webpack/plugins/env-plugin.js | 9 - .../plugins/html-minifier-webpack-plugin.js | 14 - .../html-webpack-inline-source-plugin.js | 8 - webpack/plugins/html-webpack-plugin.js | 19 - webpack/plugins/index.js | 26 - webpack/plugins/mini-css-extract-plugin.js | 12 - .../optimize-css-assets-webpack-plugin.js | 16 - .../plugins/remove-files-webpack-plugin.js | 27 - webpack/plugins/terser-plugin.js | 35 - webpack/production.js | 12 - webui.todo | 3 + 515 files changed, 68739 insertions(+), 32945 deletions(-) delete mode 100644 .babelrc create mode 100644 .nvmrc delete mode 100644 .travis.yml create mode 100644 Features.md create mode 100644 Memo/ESP3D [ESP400] format.txt create mode 100644 Memo/TargetFW.txt create mode 100644 Memo/data structure.odt create mode 100644 Memo/realtimecmd.txt create mode 100644 Notes.txt create mode 100644 config/pack.js create mode 100644 config/server.js create mode 100644 config/targets/CNC/FluidNC/index.js create mode 100644 config/targets/CNC/GRBL/index.js create mode 100644 config/targets/Printer3D/Marlin-embedded/index.js create mode 100644 config/targets/Printer3D/Marlin/index.js create mode 100644 config/targets/Printer3D/RepRap-embedded/index.js create mode 100644 config/targets/Printer3D/RepRap/index.js create mode 100644 config/targets/Printer3D/Repetier/index.js create mode 100644 config/targets/Printer3D/Smoothieware/index.js create mode 100644 config/targets/SandTable/FluidNC/index.js rename src/server/indexgrbl.js => config/targets/SandTable/GRBL/index.js (54%) create mode 100644 config/targets/SandTable/Marlin/index.js create mode 100644 config/webpack.dev.js create mode 100644 config/webpack.prod.js create mode 100644 dist/CNC/FluidNC/index.html.gz create mode 100644 dist/CNC/GRBL/index.html.gz create mode 100644 dist/Printer3D/Marlin-embedded/index.html.gz create mode 100644 dist/Printer3D/Marlin/index.html.gz create mode 100644 dist/Printer3D/RepRap-embedded/index.html.gz create mode 100644 dist/Printer3D/RepRap/index.html.gz create mode 100644 dist/Printer3D/Repetier/index.html.gz create mode 100644 dist/Printer3D/Smoothieware/index.html.gz create mode 100644 dist/SandTable/FluidNC/index.html.gz create mode 100644 dist/SandTable/GRBL/index.html.gz delete mode 100644 dist/grbl/debug/index.html.gz delete mode 100644 dist/grbl/languages/fr.json delete mode 100644 dist/grbl/languages/fr.json.gz delete mode 100644 dist/grbl/languages/zh_cn.json delete mode 100644 dist/grbl/languages/zh_cn.json.gz delete mode 100644 dist/grbl/production/index.html.gz delete mode 100644 dist/printer/debug/index.html.gz delete mode 100644 dist/printer/languages/fr.json delete mode 100644 dist/printer/languages/fr.json.gz delete mode 100644 dist/printer/languages/zh_cn.json delete mode 100644 dist/printer/languages/zh_cn.json.gz delete mode 100644 dist/printer/production/index.html.gz create mode 100644 esp3d.todo create mode 100644 extensions_samples/API.md create mode 100644 extensions_samples/capabilities.html create mode 100644 extensions_samples/dispatch.html create mode 100644 extensions_samples/download.html create mode 100644 extensions_samples/modal.html create mode 100644 extensions_samples/query.html create mode 100644 extensions_samples/sound.html create mode 100644 extensions_samples/terminal.html create mode 100644 extensions_samples/toast.html create mode 100644 extensions_samples/translate.html create mode 100644 extensions_samples/upload.html delete mode 100644 nodemon.json delete mode 100644 postcss.config.js delete mode 100644 prettyfiles.bat create mode 100644 server/CNC/FluidNC/Flash/config.yaml create mode 100644 server/Printer3D/Marlin/Flash/index.html.gz create mode 100644 server/Printer3D/Marlin/Flash/preferences.json create mode 100644 server/Printer3D/Marlin/Flash/theme-dark.gz create mode 100644 server/Printer3D/RepRap/Flash/preferences.json create mode 100644 server/public/capabilities.html create mode 100644 server/public/config.yaml create mode 100644 server/public/config.yml create mode 100644 server/public/dispatch.html create mode 100644 server/public/download.html create mode 100644 server/public/download.html.gz rename {src/server => server}/public/favicon.ico (100%) create mode 100644 server/public/image.jpg create mode 100644 server/public/index.html create mode 100644 server/public/index.html.gz rename src/languages/fr.json => server/public/lang-fr.json (100%) create mode 100644 server/public/lang-zh_cn.json.gz create mode 100644 server/public/modal.html create mode 100644 server/public/my config.yaml create mode 100644 server/public/preferences.json create mode 100644 server/public/query.html create mode 100644 server/public/sound.html create mode 100644 server/public/terminal.html create mode 100644 server/public/theme-dark.gz create mode 100644 server/public/theme-purple create mode 100644 server/public/toast.html create mode 100644 server/public/translate.html create mode 100644 server/public/upload.html rename {src/languages => server/public}/zh_cn.json (100%) create mode 100644 src/adapters/httpAdapter.js create mode 100644 src/adapters/index.js create mode 100644 src/areas/connection.js create mode 100644 src/areas/footer.js create mode 100644 src/areas/index.js rename src/{components/images/banner.js => areas/informations.js} (69%) create mode 100644 src/areas/main.js create mode 100644 src/areas/menu.js create mode 100644 src/components/App/index.js rename src/components/{ => App}/version.js (82%) create mode 100644 src/components/Controls/Button.js create mode 100644 src/components/Controls/ButtonImg.js create mode 100644 src/components/Controls/CenterLeft.js create mode 100644 src/components/Controls/Field.js create mode 100644 src/components/Controls/FieldGroup.js create mode 100644 src/components/Controls/Fields/Boolean.js create mode 100644 src/components/Controls/Fields/FormGroup.js create mode 100644 src/components/Controls/Fields/IconSelect.js create mode 100644 src/components/Controls/Fields/Input.js create mode 100644 src/components/Controls/Fields/ItemsList.js create mode 100644 src/components/Controls/Fields/Label.js create mode 100644 src/components/Controls/Fields/PickUp.js create mode 100644 src/components/Controls/Fields/Select.js create mode 100644 src/components/Controls/Fields/def_macro.json create mode 100644 src/components/Controls/Fields/def_panel.json create mode 100644 src/components/Controls/Fields/index.js create mode 100644 src/components/Controls/Loading.js create mode 100644 src/components/Controls/Modal.js create mode 100644 src/components/Controls/Progress.js create mode 100644 src/components/Controls/ScanAp.js create mode 100644 src/components/Controls/ScanPacksList.js create mode 100644 src/components/Controls/Toast.js create mode 100644 src/components/Controls/index.js create mode 100644 src/components/ExtraContent/index.js create mode 100644 src/components/Helpers/arrays.js create mode 100644 src/components/Helpers/components.js create mode 100644 src/components/Helpers/filters.js create mode 100644 src/components/Helpers/html.js create mode 100644 src/components/Helpers/http.js create mode 100644 src/components/Helpers/index.js create mode 100644 src/components/Helpers/strings.js create mode 100644 src/components/Helpers/time.js create mode 100644 src/components/Modal/confirmModal.js create mode 100644 src/components/Modal/genericModal.js create mode 100644 src/components/Modal/index.js create mode 100644 src/components/Modal/keepConnectedModal.js create mode 100644 src/components/Modal/logginModal.js create mode 100644 src/components/Modal/progressModal.js create mode 100644 src/components/Navbar/index.js create mode 100644 src/components/Panels/Charts.js create mode 100644 src/components/Panels/ExtraControls.js create mode 100644 src/components/Panels/ExtraPanel.js create mode 100644 src/components/Panels/Extruders.js create mode 100644 src/components/Panels/Files.js create mode 100644 src/components/Panels/Jog.js create mode 100644 src/components/Panels/JogCNC.js create mode 100644 src/components/Panels/Macros.js create mode 100644 src/components/Panels/Notifications.js create mode 100644 src/components/Panels/Status.js create mode 100644 src/components/Panels/Temperatures.js create mode 100644 src/components/Panels/Terminal.js create mode 100644 src/components/Panels/index.js create mode 100644 src/components/Router/index.js create mode 100644 src/components/TabBar/index.js create mode 100644 src/components/Toast/index.js delete mode 100644 src/components/about/index.js delete mode 100644 src/components/app.js delete mode 100644 src/components/audio/index.js delete mode 100644 src/components/dashboard/extrapanels.js delete mode 100644 src/components/dashboard/index.js delete mode 100644 src/components/dashboard/macros.js delete mode 100644 src/components/dashboard/terminal.js delete mode 100644 src/components/dialog/index.js delete mode 100644 src/components/grbl/files.js delete mode 100644 src/components/grbl/index.js delete mode 100644 src/components/grbl/jog.js delete mode 100644 src/components/grbl/preferences.js delete mode 100644 src/components/grbl/store/axis.js delete mode 100644 src/components/grbl/store/index.js delete mode 100644 src/components/grbl/store/positions.js delete mode 100644 src/components/header/index.js delete mode 100644 src/components/http/index.js create mode 100644 src/components/images/icons.js delete mode 100644 src/components/notification/index.js delete mode 100644 src/components/printer/en.json delete mode 100644 src/components/printer/extrusion.js delete mode 100644 src/components/printer/fan.js delete mode 100644 src/components/printer/feedrate.js delete mode 100644 src/components/printer/files.js delete mode 100644 src/components/printer/flowrate.js delete mode 100644 src/components/printer/index.js delete mode 100644 src/components/printer/jog.js delete mode 100644 src/components/printer/notifications.js delete mode 100644 src/components/printer/preferences.js delete mode 100644 src/components/printer/sensors.js delete mode 100644 src/components/printer/smoothie.js delete mode 100644 src/components/printer/status.js delete mode 100644 src/components/printer/store/display.js delete mode 100644 src/components/printer/store/fan.js delete mode 100644 src/components/printer/store/feedrate.js delete mode 100644 src/components/printer/store/flowrate.js delete mode 100644 src/components/printer/store/index.js delete mode 100644 src/components/printer/store/positions.js delete mode 100644 src/components/printer/store/sensor.js delete mode 100644 src/components/printer/store/status.js delete mode 100644 src/components/printer/store/temperatures.js delete mode 100644 src/components/printer/temperatures.js delete mode 100644 src/components/settings/esp3d.js delete mode 100644 src/components/settings/index.js delete mode 100644 src/components/settings/selector.js delete mode 100644 src/components/settings/webui.js delete mode 100644 src/components/settings/wizard.js delete mode 100644 src/components/store/dialogcontent.js delete mode 100644 src/components/store/display.js delete mode 100644 src/components/store/error.js delete mode 100644 src/components/store/filespanel.js delete mode 100644 src/components/store/monitor.js delete mode 100644 src/components/store/notificationsbar.js delete mode 100644 src/components/translations/en.json create mode 100644 src/components/translations/languages.json delete mode 100644 src/components/websocket/index.js create mode 100644 src/contexts/DatasContext.js create mode 100644 src/contexts/HttpQueueContext.js create mode 100644 src/contexts/RouterContext.js create mode 100644 src/contexts/SettingsContext.js create mode 100644 src/contexts/UiContext.js create mode 100644 src/contexts/WsContext.js create mode 100644 src/contexts/index.js create mode 100644 src/hooks/index.js create mode 100644 src/hooks/useHttpQueue.js create mode 100644 src/hooks/useSettings.js delete mode 100644 src/languages/build.js delete mode 100644 src/languages/grbl/fr.json delete mode 100644 src/languages/language-list.json delete mode 100644 src/languages/printer/fr.json create mode 100644 src/pages/about/index.js create mode 100644 src/pages/dashboard/index.js create mode 100644 src/pages/extrapages/index.js create mode 100644 src/pages/settings/index.js delete mode 100644 src/server/index.js delete mode 100644 src/server/public/fr.json delete mode 100644 src/server/public/hello.html delete mode 100644 src/server/public/index.html.gz delete mode 100644 src/server/public/myfolder/favicon.ico delete mode 100644 src/server/public/myfolder/index.html.gz delete mode 100644 src/server/public/oem.json.tpl delete mode 100644 src/server/public/preferences.json create mode 100644 src/style/_mixins.scss create mode 100644 src/style/_spectre-exp.scss create mode 100644 src/style/_spectre-icons.scss create mode 100644 src/style/_spectre.scss create mode 100644 src/style/_variables.scss create mode 100644 src/style/components/_app.scss create mode 100644 src/style/components/_control.scss create mode 100644 src/style/components/_media.scss create mode 100644 src/style/components/_menu.scss create mode 100644 src/style/components/_navbar.scss create mode 100644 src/style/components/_panel.scss create mode 100644 src/style/index.scss delete mode 100644 src/stylesheets/_bootstrap.scss delete mode 100644 src/stylesheets/_functions.scss delete mode 100644 src/stylesheets/_vars.scss delete mode 100644 src/stylesheets/application.scss delete mode 100644 src/stylesheets/components/_banner.scss delete mode 100644 src/stylesheets/components/_controls.scss delete mode 100644 src/stylesheets/components/_modaldlg.scss delete mode 100644 src/stylesheets/components/_navbar.scss delete mode 100644 src/stylesheets/components/_notification.scss delete mode 100644 src/stylesheets/components/grbl/_grbl.scss delete mode 100644 src/stylesheets/components/printer/_printer.scss create mode 100644 src/tabs/features/exportHelper.js create mode 100644 src/tabs/features/formatHelper.js create mode 100644 src/tabs/features/importHelper.js create mode 100644 src/tabs/features/index.js create mode 100644 src/tabs/interface/exportHelper.js create mode 100644 src/tabs/interface/importHelper.js create mode 100644 src/tabs/interface/index.js create mode 100644 src/tabs/machine/index.js create mode 100644 src/targets/CNC/Controls/EmergencyButton.js create mode 100644 src/targets/CNC/Controls/index.js create mode 100644 src/targets/CNC/FluidNC/CMD-source.js create mode 100644 src/targets/CNC/FluidNC/Controls/InformationsControls.js create mode 100644 src/targets/CNC/FluidNC/Controls/QuickButtonsBar.js create mode 100644 src/targets/CNC/FluidNC/Controls/index.js create mode 100644 src/targets/CNC/FluidNC/DIRECTSD-source.js create mode 100644 src/targets/CNC/FluidNC/MachineSettings.js create mode 100644 src/targets/CNC/FluidNC/TargetContext.js create mode 100644 src/targets/CNC/FluidNC/exportHelper.js create mode 100644 src/targets/CNC/FluidNC/files.js create mode 100644 src/targets/CNC/FluidNC/icons.js create mode 100644 src/targets/CNC/FluidNC/importHelper.js create mode 100644 src/targets/CNC/FluidNC/index.js create mode 100644 src/targets/CNC/FluidNC/logo.js create mode 100644 src/targets/CNC/FluidNC/panels.js create mode 100644 src/targets/CNC/FluidNC/preferences.json create mode 100644 src/targets/CNC/FluidNC/processor.js create mode 100644 src/targets/CNC/FluidNC/realCommandsTable.js create mode 100644 src/targets/CNC/FluidNC/stream.js create mode 100644 src/targets/CNC/FluidNC/style/index.scss create mode 100644 src/targets/CNC/FluidNC/translations/en.json create mode 100644 src/targets/CNC/GRBL/CMD-source.js create mode 100644 src/targets/CNC/GRBL/Controls/InformationsControls.js create mode 100644 src/targets/CNC/GRBL/Controls/QuickButtonsBar.js create mode 100644 src/targets/CNC/GRBL/Controls/index.js create mode 100644 src/targets/CNC/GRBL/MachineSettings.js create mode 100644 src/targets/CNC/GRBL/TargetContext.js create mode 100644 src/targets/CNC/GRBL/files.js create mode 100644 src/targets/CNC/GRBL/icons.js create mode 100644 src/targets/CNC/GRBL/index.js create mode 100644 src/targets/CNC/GRBL/panels.js create mode 100644 src/targets/CNC/GRBL/preferences.json create mode 100644 src/targets/CNC/GRBL/processor.js create mode 100644 src/targets/CNC/GRBL/realCommandsTable.js create mode 100644 src/targets/CNC/GRBL/stream.js create mode 100644 src/targets/CNC/GRBL/style/index.scss rename src/{components/grbl => targets/CNC/GRBL/translations}/en.json (76%) create mode 100644 src/targets/CNC/preferences.json create mode 100644 src/targets/CNC/style/_index.scss create mode 100644 src/targets/CNC/translations/en.json create mode 100644 src/targets/FLASH-source.js create mode 100644 src/targets/Printer3D/Controls/EmergencyButton.js create mode 100644 src/targets/Printer3D/Controls/index.js create mode 100644 src/targets/Printer3D/Marlin-embedded/CMD-source.js create mode 100644 src/targets/Printer3D/Marlin-embedded/Controls/InformationsControls.js create mode 100644 src/targets/Printer3D/Marlin-embedded/Controls/MixedExtrudersControl.js create mode 100644 src/targets/Printer3D/Marlin-embedded/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/Marlin-embedded/Controls/index.js create mode 100644 src/targets/Printer3D/Marlin-embedded/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/Marlin-embedded/MachineSettings.js create mode 100644 src/targets/Printer3D/Marlin-embedded/SD-source.js create mode 100644 src/targets/Printer3D/Marlin-embedded/TFT-SD-source.js create mode 100644 src/targets/Printer3D/Marlin-embedded/TFT-USB-source.js create mode 100644 src/targets/Printer3D/Marlin-embedded/TargetContext.js create mode 100644 src/targets/Printer3D/Marlin-embedded/files.js create mode 100644 src/targets/Printer3D/Marlin-embedded/filters.js rename src/{components/printer/icon.js => targets/Printer3D/Marlin-embedded/icons.js} (61%) create mode 100644 src/targets/Printer3D/Marlin-embedded/index.js create mode 100644 src/targets/Printer3D/Marlin-embedded/logo.js create mode 100644 src/targets/Printer3D/Marlin-embedded/panels.js create mode 100644 src/targets/Printer3D/Marlin-embedded/preferences.json create mode 100644 src/targets/Printer3D/Marlin-embedded/processor.js create mode 100644 src/targets/Printer3D/Marlin-embedded/stream.js create mode 100644 src/targets/Printer3D/Marlin-embedded/style/index.scss create mode 100644 src/targets/Printer3D/Marlin-embedded/translations/en.json create mode 100644 src/targets/Printer3D/Marlin/CMD-source.js create mode 100644 src/targets/Printer3D/Marlin/Controls/InformationsControls.js create mode 100644 src/targets/Printer3D/Marlin/Controls/MixedExtrudersControl.js create mode 100644 src/targets/Printer3D/Marlin/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/Marlin/Controls/index.js create mode 100644 src/targets/Printer3D/Marlin/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/Marlin/MachineSettings.js create mode 100644 src/targets/Printer3D/Marlin/SD-source.js create mode 100644 src/targets/Printer3D/Marlin/TFT-SD-source.js create mode 100644 src/targets/Printer3D/Marlin/TFT-USB-source.js create mode 100644 src/targets/Printer3D/Marlin/TargetContext.js create mode 100644 src/targets/Printer3D/Marlin/files.js create mode 100644 src/targets/Printer3D/Marlin/filters.js create mode 100644 src/targets/Printer3D/Marlin/icons.js create mode 100644 src/targets/Printer3D/Marlin/index.js create mode 100644 src/targets/Printer3D/Marlin/panels.js create mode 100644 src/targets/Printer3D/Marlin/preferences.json create mode 100644 src/targets/Printer3D/Marlin/processor.js create mode 100644 src/targets/Printer3D/Marlin/stream.js create mode 100644 src/targets/Printer3D/Marlin/style/index.scss create mode 100644 src/targets/Printer3D/Marlin/translations/en.json create mode 100644 src/targets/Printer3D/RepRap-embedded/CMD-source.js create mode 100644 src/targets/Printer3D/RepRap-embedded/Controls/InformationsControls.js rename src/{components/grbl/notifications.js => targets/Printer3D/RepRap-embedded/Controls/MixedExtrudersControl.js} (67%) create mode 100644 src/targets/Printer3D/RepRap-embedded/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/RepRap-embedded/Controls/index.js create mode 100644 src/targets/Printer3D/RepRap-embedded/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/RepRap-embedded/MachineSettings.js create mode 100644 src/targets/Printer3D/RepRap-embedded/SD-source.js create mode 100644 src/targets/Printer3D/RepRap-embedded/TargetContext.js create mode 100644 src/targets/Printer3D/RepRap-embedded/files.js create mode 100644 src/targets/Printer3D/RepRap-embedded/filters.js create mode 100644 src/targets/Printer3D/RepRap-embedded/icons.js create mode 100644 src/targets/Printer3D/RepRap-embedded/index.js create mode 100644 src/targets/Printer3D/RepRap-embedded/panels.js create mode 100644 src/targets/Printer3D/RepRap-embedded/preferences.json create mode 100644 src/targets/Printer3D/RepRap-embedded/processor.js create mode 100644 src/targets/Printer3D/RepRap-embedded/stream.js create mode 100644 src/targets/Printer3D/RepRap-embedded/style/index.scss create mode 100644 src/targets/Printer3D/RepRap-embedded/translations/en.json create mode 100644 src/targets/Printer3D/RepRap/CMD-source.js create mode 100644 src/targets/Printer3D/RepRap/Controls/InformationsControls.js create mode 100644 src/targets/Printer3D/RepRap/Controls/MixedExtrudersControl.js create mode 100644 src/targets/Printer3D/RepRap/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/RepRap/Controls/index.js create mode 100644 src/targets/Printer3D/RepRap/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/RepRap/MachineSettings.js create mode 100644 src/targets/Printer3D/RepRap/SD-source.js create mode 100644 src/targets/Printer3D/RepRap/TargetContext.js create mode 100644 src/targets/Printer3D/RepRap/files.js create mode 100644 src/targets/Printer3D/RepRap/filters.js create mode 100644 src/targets/Printer3D/RepRap/icons.js create mode 100644 src/targets/Printer3D/RepRap/index.js create mode 100644 src/targets/Printer3D/RepRap/panels.js create mode 100644 src/targets/Printer3D/RepRap/preferences.json create mode 100644 src/targets/Printer3D/RepRap/processor.js create mode 100644 src/targets/Printer3D/RepRap/stream.js create mode 100644 src/targets/Printer3D/RepRap/style/index.scss create mode 100644 src/targets/Printer3D/RepRap/translations/en.json create mode 100644 src/targets/Printer3D/Repetier/CMD-source.js create mode 100644 src/targets/Printer3D/Repetier/Controls/InformationsControls.js create mode 100644 src/targets/Printer3D/Repetier/Controls/MixedExtrudersControl.js create mode 100644 src/targets/Printer3D/Repetier/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/Repetier/Controls/index.js create mode 100644 src/targets/Printer3D/Repetier/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/Repetier/MachineSettings.js create mode 100644 src/targets/Printer3D/Repetier/SD-source.js create mode 100644 src/targets/Printer3D/Repetier/TFT-SD-source.js create mode 100644 src/targets/Printer3D/Repetier/TFT-USB-source.js create mode 100644 src/targets/Printer3D/Repetier/TargetContext.js create mode 100644 src/targets/Printer3D/Repetier/files.js create mode 100644 src/targets/Printer3D/Repetier/filters.js create mode 100644 src/targets/Printer3D/Repetier/icons.js create mode 100644 src/targets/Printer3D/Repetier/index.js create mode 100644 src/targets/Printer3D/Repetier/panels.js create mode 100644 src/targets/Printer3D/Repetier/preferences.json create mode 100644 src/targets/Printer3D/Repetier/processor.js create mode 100644 src/targets/Printer3D/Repetier/stream.js create mode 100644 src/targets/Printer3D/Repetier/style/index.scss create mode 100644 src/targets/Printer3D/Repetier/translations/en.json create mode 100644 src/targets/Printer3D/Smoothieware/CMD-source.js create mode 100644 src/targets/Printer3D/Smoothieware/Controls/InformationsControls.js create mode 100644 src/targets/Printer3D/Smoothieware/Controls/MixedExtrudersControl.js create mode 100644 src/targets/Printer3D/Smoothieware/Controls/QuickButtonsBar.js create mode 100644 src/targets/Printer3D/Smoothieware/Controls/index.js create mode 100644 src/targets/Printer3D/Smoothieware/DIRECTSD-source.js create mode 100644 src/targets/Printer3D/Smoothieware/DIRECTSDEXT-source.js create mode 100644 src/targets/Printer3D/Smoothieware/MachineSettings.js create mode 100644 src/targets/Printer3D/Smoothieware/SD-source.js create mode 100644 src/targets/Printer3D/Smoothieware/SDEXT-source.js create mode 100644 src/targets/Printer3D/Smoothieware/TFT-SD-source.js create mode 100644 src/targets/Printer3D/Smoothieware/TFT-USB-source.js create mode 100644 src/targets/Printer3D/Smoothieware/TargetContext.js create mode 100644 src/targets/Printer3D/Smoothieware/files.js create mode 100644 src/targets/Printer3D/Smoothieware/filters.js create mode 100644 src/targets/Printer3D/Smoothieware/icons.js create mode 100644 src/targets/Printer3D/Smoothieware/index.js create mode 100644 src/targets/Printer3D/Smoothieware/panels.js create mode 100644 src/targets/Printer3D/Smoothieware/preferences.json create mode 100644 src/targets/Printer3D/Smoothieware/processor.js create mode 100644 src/targets/Printer3D/Smoothieware/stream.js create mode 100644 src/targets/Printer3D/Smoothieware/style/index.scss create mode 100644 src/targets/Printer3D/Smoothieware/translations/en.json create mode 100644 src/targets/Printer3D/icon.js create mode 100644 src/targets/Printer3D/preferences.json create mode 100644 src/targets/Printer3D/style/_index.scss create mode 100644 src/targets/Printer3D/translations/en.json create mode 100644 src/targets/SandTable/Controls/EmergencyButton.js create mode 100644 src/targets/SandTable/Controls/index.js create mode 100644 src/targets/SandTable/FluidNC/CMD-source.js create mode 100644 src/targets/SandTable/FluidNC/Controls/InformationsControls.js create mode 100644 src/targets/SandTable/FluidNC/Controls/QuickButtonsBar.js create mode 100644 src/targets/SandTable/FluidNC/Controls/index.js create mode 100644 src/targets/SandTable/FluidNC/DIRECTSD-source.js create mode 100644 src/targets/SandTable/FluidNC/MachineSettings.js create mode 100644 src/targets/SandTable/FluidNC/TargetContext.js create mode 100644 src/targets/SandTable/FluidNC/exportHelper.js create mode 100644 src/targets/SandTable/FluidNC/files.js create mode 100644 src/targets/SandTable/FluidNC/icons.js create mode 100644 src/targets/SandTable/FluidNC/importHelper.js create mode 100644 src/targets/SandTable/FluidNC/index.js create mode 100644 src/targets/SandTable/FluidNC/logo.js create mode 100644 src/targets/SandTable/FluidNC/panels.js create mode 100644 src/targets/SandTable/FluidNC/preferences.json create mode 100644 src/targets/SandTable/FluidNC/processor.js create mode 100644 src/targets/SandTable/FluidNC/realCommandsTable.js create mode 100644 src/targets/SandTable/FluidNC/stream.js create mode 100644 src/targets/SandTable/FluidNC/style/index.scss create mode 100644 src/targets/SandTable/FluidNC/translations/en.json create mode 100644 src/targets/SandTable/GRBL/CMD-source.js create mode 100644 src/targets/SandTable/GRBL/Controls/InformationsControls.js create mode 100644 src/targets/SandTable/GRBL/Controls/QuickButtonsBar.js create mode 100644 src/targets/SandTable/GRBL/Controls/index.js create mode 100644 src/targets/SandTable/GRBL/MachineSettings.js create mode 100644 src/targets/SandTable/GRBL/TargetContext.js create mode 100644 src/targets/SandTable/GRBL/files.js create mode 100644 src/targets/SandTable/GRBL/icons.js create mode 100644 src/targets/SandTable/GRBL/index.js create mode 100644 src/targets/SandTable/GRBL/panels.js create mode 100644 src/targets/SandTable/GRBL/preferences.json create mode 100644 src/targets/SandTable/GRBL/processor.js create mode 100644 src/targets/SandTable/GRBL/realCommandsTable.js create mode 100644 src/targets/SandTable/GRBL/stream.js create mode 100644 src/targets/SandTable/GRBL/style/index.scss create mode 100644 src/targets/SandTable/GRBL/translations/en.json create mode 100644 src/targets/SandTable/preferences.json create mode 100644 src/targets/SandTable/style/_index.scss create mode 100644 src/targets/SandTable/translations/en.json create mode 100644 src/targets/helpers.js create mode 100644 src/targets/index.js create mode 100644 src/targets/preferences.json create mode 100644 src/targets/translations/en.json create mode 100644 themes_samples/theme-dark.gz create mode 100644 themes_samples/theme-purple delete mode 100644 webpack.config.js delete mode 100644 webpack/development.js delete mode 100644 webpack/environment.js delete mode 100644 webpack/plugins/compression-plugin.js delete mode 100644 webpack/plugins/env-plugin.js delete mode 100644 webpack/plugins/html-minifier-webpack-plugin.js delete mode 100644 webpack/plugins/html-webpack-inline-source-plugin.js delete mode 100644 webpack/plugins/html-webpack-plugin.js delete mode 100644 webpack/plugins/index.js delete mode 100644 webpack/plugins/mini-css-extract-plugin.js delete mode 100644 webpack/plugins/optimize-css-assets-webpack-plugin.js delete mode 100644 webpack/plugins/remove-files-webpack-plugin.js delete mode 100644 webpack/plugins/terser-plugin.js delete mode 100644 webpack/production.js create mode 100644 webui.todo diff --git a/.babelrc b/.babelrc deleted file mode 100644 index e15d3ec9..00000000 --- a/.babelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "presets": [ - "@babel/preset-env", - ["@babel/preset-react", { "pragma": "h", "pragmaFrag": "Fragment" }] - ] -} diff --git a/.gitignore b/.gitignore index f403355b..d3abe3b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ + node_modules -dist/images/jogdial.svg -dist/images/jogdial1.svg -dist/images/jogdial2.svg -dist/images/printer.tpl.htm -dist/images/repetier.htm -dist/index.html +.vscode +build diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..e8baaea6 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14.16.1 \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 52de2094..118b32be 100644 --- a/.prettierrc +++ b/.prettierrc @@ -11,7 +11,7 @@ }, {"files": "*.scss", "options": { - "parser": "scss" + "parser": "css" } } ] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 726db02e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: node_js -node_js: - - "lts/*" -cache: - directories: - - "node_modules" -sudo: false -install: - - npm install -script: - - echo "build index.html" - - npm run build - -notifications: - email: - on_success: change - on_failure: change diff --git a/ESP3D-WebUI-features.xls b/ESP3D-WebUI-features.xls index 291111a7e1f2324060f2ed29847f4dc90bab95bd..11bcf63f56510510630f09544b01f155177c1db7 100644 GIT binary patch delta 27 ecmZq3X~@~&t-~kA00N8*VnE37e{-bHa!vqRNCr3n delta 27 hcmZq3X~@~&t;5H{z`?-4z{tP?gd7~3BXyQ@0svBg1sebW diff --git a/Features.md b/Features.md new file mode 100644 index 00000000..c68a6bfd --- /dev/null +++ b/Features.md @@ -0,0 +1,73 @@ +# V3 Features + +## Global Features +* Firmware update +* WebUI update +* List of enabled features /capabilities +* Wifi configuration +* Features configuration +* WebUI features configuration +* Haptic feedback +* Audio feedback +* Terminal commands +* Emmergency stop +* Pluggins support +* Themes support (CSS) +* Language packs support +* Macro commands +* Local FS listing / content management +* External pages/panel support +* PC / Tablet / phone UI +* IP Camera & ESP32 Camera display support +* Import / Export settings +* Restart board support +* Monolitic small footprint +* Single user management support (auto close if not latest connected) +* Authentication support (admin / user) +* Configuration wizard (TBD) +* Firmware supported (3DPrinter / CNC / SandTable): + * ESP3D: + * Marlin (1.x / 2.x) + * Smoothieware (1.x / 2.x) + * Repetier (1.x / 2.x) + * GRBL (TBD) + * ESP3DLib + * Marlin (2.x) + * GRBL_ESP32 (TBD) + * FluidNC (TBD) + * Makerbase TFT (ESP3D) + * Bigtreetech TFT (ESP3D) +* Firmware NOT yet supported: + * Reprap + +## 3D Printer Features + * Target firmware configuration + * Jog control / monitoring + * Temperatures control / monitoring + * Additionnal sensors support + * Chart support for temperatures / sensors + * Multiple extruder support + * Target Firmware SD listing / content management (if supported) + * TFT SD/USB listing / content management (if supported) + * Fan control / monitoring (if supported) + * Flow rate control / monitoring (if supported) + * Feed rate control / monitoring (if supported) + * Print control / monitoring + +## CNC Features + * Target firmware configuration + * Jog control / monitoring + * Job control / monitoring + * Status monitoring + * TBD... + +## Sand Table Features + * Target firmware configuration + * Jog control / monitoring + * Job control / monitoring + * Status monitoring + * TBD... + + + + diff --git a/Memo/ESP3D [ESP400] format.txt b/Memo/ESP3D [ESP400] format.txt new file mode 100644 index 00000000..be7c185d --- /dev/null +++ b/Memo/ESP3D [ESP400] format.txt @@ -0,0 +1,92 @@ +ESP3D [ESP400] format: +{"Settings":[ +{"F":"network/network","P":"130","T":"S","V":"esp3d","H":"hostname","S":"32","M":"1"}, +{"F":"network/network","P":"0","T":"B","V":"1","H":"radio mode","O":[{"none":"0"}, +{"sta":"1"}, +{"ap":"2"}]}, +{"F":"network/sta","P":"1","T":"S","V":"WIFI_OFFICE_B2G","S":"32","H":"SSID","M":"1"}, +{"F":"network/sta","P":"34","T":"S","N":"1","V":"********","S":"64","H":"pwd","M":"8"}, +{"F":"network/sta","P":"99","T":"B","V":"1","H":"ip mode","O":[{"dhcp":"1"}, +{"static":"0"}]}, +{"F":"network/sta","P":"100","T":"A","V":"192.168.0.1","H":"ip"}, +{"F":"network/sta","P":"108","T":"A","V":"192.168.0.1","H":"gw"}, +{"F":"network/sta","P":"104","T":"A","V":"255.255.255.0","H":"msk"}, +{"F":"network/ap","P":"218","T":"S","V":"ESP3D","S":"32","H":"SSID","M":"1"}, +{"F":"network/ap","P":"251","T":"S","N":"1","V":"********","S":"64","H":"pwd","M":"8"}, +{"F":"network/ap","P":"316","T":"A","V":"192.168.0.1","H":"ip"}, +{"F":"network/ap","P":"118","T":"B","V":"11","H":"channel","O":[{"1":"1"}, +{"2":"2"}, +{"3":"3"}, +{"4":"4"}, +{"5":"5"}, +{"6":"6"}, +{"7":"7"}, +{"8":"8"}, +{"9":"9"}, +{"10":"10"}, +{"11":"11"}, +{"12":"12"}, +{"13":"13"}, +{"14":"14"}]}, +{"F":"service/http","P":"328","T":"B","V":"1","H":"enable","O":[{"no":"0"}, +{"yes":"1"}]}, +{"F":"service/http","P":"121","T":"I","V":"80","H":"port","S":"65001","M":"1"}, +{"F":"service/telnetp","P":"329","T":"B","V":"1","H":"enable","O":[{"no":"0"}, +{"yes":"1"}]}, +{"F":"service/telnetp","P":"125","T":"I","V":"23","H":"port","S":"65001","M":"1"}, +{"F":"service/ftp","P":"1021","T":"B","V":"1","H":"enable","O":[{"no":"0"}, +{"yes":"1"}]}, +{"F":"service/ftp","P":"1009","T":"I","V":"21","H":"control port","S":"65001","M":"1"}, +{"F":"service/ftp","P":"1013","T":"I","V":"20","H":"active port","S":"65001","M":"1"}, +{"F":"service/ftp","P":"1017","T":"I","V":"55600","H":"passive port","S":"65001","M":"1"}, +{"F":"service/notification","P":"1004","T":"B","V":"1","H":"auto notif","O":[{"no":"0"}, +{"yes":"1"}]}, +{"F":"service/notification","P":"116","T":"B","V":"0","H":"notification","O":[{"none":"0"}, +{"pushover":"1"}, +{"email":"2"}, +{"line":"3"}]}, +{"F":"service/notification","P":"332","T":"S","V":"********","S":"63","H":"t1","M":"0"}, +{"F":"service/notification","P":"396","T":"S","V":"********","S":"63","H":"t2","M":"0"}, +{"F":"service/notification","P":"855","T":"S","V":" ","S":"127","H":"ts","M":"0"}, +{"F":"system/system","P":"461","T":"B","V":"40","H":"targetfw","O":[{"repetier":"50"}, +{"marlin":"20"}, +{"smoothieware":"40"}, +{"grbl":"10"}, +{"unknown":"0"}]}, +{"F":"system/system","P":"112","T":"I","V":"115200","H":"baud","O":[{"9600":"9600"}, +{"19200":"19200"}, +{"38400":"38400"}, +{"57600":"57600"}, +{"74880":"74880"}, +{"115200":"115200"}, +{"230400":"230400"}, +{"250000":"250000"}, +{"500000":"500000"}, +{"921600":"921600"}]}, +{"F":"system/system","P":"320","T":"I","V":"10000","H":"bootdelay","S":"40000","M":"0"}, +]} + +1 - key : Settings +2 - value: array of data formated like this +{"F":"network/network","P":"130","T":"S","V":"esp3d","H":"hostname","S":"32","M":"1"} +or +{"F":"service/notification","P":"1004","T":"B","V":"1","H":"auto notif","O":[{"no":"0"},{"yes":"1"}]} + +F: is filter formated as section/sub-section, if section is same as sub-section, it means no sub-section +P: is id (also position reference so it is unique) +T: is type of data which can be: +S: for string +I: for integer +T: for Byte +A: for IP address / Mask +V: is current value, if type is string and value is ********, (8 stars)then it is a password +H: is label of value +S: is max size if type is string, and max possible value if value is number (byte, integer) +M: is min size if type is string, and min possible value if value is number (byte, integer) +MS: is additionnal min size if type is string (e.g for password can be 0 or 8, so need additional min size), M should be the more minimal value +so MS value must be between M and S +O: is an array of {label:value} +R: need restart to be applied + +Note: currently there is no float value so far, if needed a new type will be added in futur - TBD, +Note2 : the 2.1 Flag type is no more used, several entries are used instead grouped by sub-section diff --git a/Memo/TargetFW.txt b/Memo/TargetFW.txt new file mode 100644 index 00000000..f6473a84 --- /dev/null +++ b/Memo/TargetFW.txt @@ -0,0 +1,7 @@ +fluidnc: 60 +repetier: 50 +smoothieware: 40 +marlin_embedded: 30 +marlin: 20 +grbl: 10 +unknown: 0 \ No newline at end of file diff --git a/Memo/data structure.odt b/Memo/data structure.odt new file mode 100644 index 0000000000000000000000000000000000000000..8c540c44e7b3230e5409b5ebc9b872f7ed7ad33b GIT binary patch literal 18339 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 +0x99 : Set 100% of programmed spindle speed #SSO100# +0x9A : Increase 10% #SSO+10# +0x9B : Decrease 10% #SSO-10# +0x9C : Increase 1% #SSO+1# +0x9D : Decrease 1% #SSO-1# + +0x9E : Toggle Spindle Stop #T-SPINDLESTOP# +0xA0 : Toggle Flood Coolant #T-FLOODCOOLANT# +0xA1 : Toggle Mist Coolant #T-MISTCOOLANT# + +[{name:"#SOFTRESET#", value:"\x18"}, + {name:"#SAFETYDOOR#", value:"\x84"}, + {name:"#JOGCANCEL#", value:"\x85"}, + {name:"#FO100#", value:"\x90"}, + {name:"#FO+10#", value:"\x91"}, + {name:"#FO-10#", value:"\x92"}, + {name:"#FO+1#", value:"\x93"}, + {name:"#FO-1#", value:"\x94"}, + {name:"#RO100#", value:"\x95"}, + {name:"#RO50#", value:"\x96"}, + {name:"#RO25#", value:"\x97"}, + {name:"#SSO100#", value:"\x99"}, + {name:"#SSO+10#", value:"\x9A"}, + {name:"#SSO-10#", value:"\x9B"}, + {name:"#SSO+1#", value:"\x9C"}, + {name:"#SSO-1#", value:"\x9D"}, + {name:"#T-SPINDLESTOP#", value:"\x9E"}, + {name:"#T-FLOODCOOLANT#", value:"\xA0"}, + {name:"#T-FLOODCOOLANT#", value:"\xA1"} +] \ No newline at end of file diff --git a/Notes.txt b/Notes.txt new file mode 100644 index 00000000..e28f7c91 --- /dev/null +++ b/Notes.txt @@ -0,0 +1,16 @@ +* Fix dev websocket server cannot work under Linux +> sudo apt-get install libcap2-bin +> sudo setcap cap_net_bind_service=+ep `readlink -f \`which node\`` + +* Fix ‘ and “ need space to be displayed under Linux +> setxkbmap -layout us + +* Fix for sass div warning +>npm install -g sass-migrator +>sass-migrator division **/*.scss +Note: +on windows need change script policy first: +> Get-ExecutionPolicy -List +>Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +then when done, put back default (the one got by initial Get-ExecutionPolicy -List) +>Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope CurrentUser diff --git a/README.md b/README.md index 19884ae5..5100cb76 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,54 @@ # ESP3D-WEBUI 3.0 -using Preact per @aganov suggestion -## In alpha stage - ready to test... but be noted it will soon be replaced by rewrited version : https://github.com/luc-github/ESP3D-WEBUI/tree/3.0-rewrite +using Preact per @aganov suggestion +Rewrite per @alxblog suggestion to use proper Preactjs API and lighter code: use spectre.css instead of bootstrap 4.x + +## In development / pre-alpha stage - NOT ready to test... + +Only compatible with [ESP3DLib v2](https://github.com/luc-github/ESP3DLib/tree/2-0) - which will be merged to [ESP3D 3.0](https://github.com/luc-github/ESP3D/tree/3.0) later + +[Latest development version ![Development Version](https://img.shields.io/badge/Devt-v3.0-yellow?style=plastic)](https://github.com/luc-github/ESP3D-WEBUI/tree/3.0-rewrite) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/luc-github/ESP3D-WEBUI/3.0-rewrite?style=plastic) [![Travis (.org) branch](https://img.shields.io/travis/luc-github/ESP3D-WEBUI/3.0-rewrite?style=plastic)](https://travis-ci.org/github/luc-github/ESP3D-WEBUI) - [Latest development version ![Development Version](https://img.shields.io/badge/Devt-v3.0-yellow?style=plastic)](https://github.com/luc-github/ESP3D-WEBUI/tree/3.0) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/luc-github/ESP3D-WEBUI/3.0?style=plastic) ![Travis (.org) branch](https://img.shields.io/travis/luc-github/ESP3D-WEBUI/3.0?style=plastic) - ### Setup development tools -1 - Install current nodejs LTS (currently using v12.18.3) +1 - Install current nodejs LTS (currently using v14.16.1) + ``` node -v -v12.18.3 +v14.16.1 npm -v -6.14.6 +6.14.12 ``` - + 2 - Download all necessary packages in ESP3D-WEBUI directory (repository root) -``` -npm install -``` -### Start dev server -in ESP3D-WEBUI directory (repository root) ``` -npm run dev +npm install ``` -will open http://localhost:3000 -Back end query server is localhost:8080, websocket server is localhost:81 -### Build index.html.gz to /dist folder -in ESP3D-WEBUI directory (repository root) -``` -npm run build -``` -Will generate production and debug versions for grbl and printer +### Start dev server +in ESP3D-WEBUI directory (repository root) ``` -npm run build-debug +npm run dev ``` -Will generate debug versions for grbl and printer, which contain all console.log message. +will open http://localhost:8088 -``` -npm run build-prod -``` -Will generate production versions for grbl and printer, cleaned from all `console.log` messages. - -you can even go to more atomic level with : `npm run build-printer`, `npm run build-grbl`, `npm run build-printer-debug`, `npm run build-grbl-debug`, `npm run build-lang` +Back end query server is localhost:8080, websocket server is localhost:81 +### Build index.html.gz to /dist folder -### Code beautify with prettier -use pluggin or cli +in ESP3D-WEBUI directory (repository root) ``` -npm install --global prettier +npm run build ``` -the config file is .prettierrc -a batch is available if you need : prettyfiles.bat, it is just automating the following commands: -prettier --config .prettierrc --write "{src/**/,webpack/**/}*.js" -prettier --config .prettierrc --write src/**/*.html" +Will generate production version # Chat -ESP3D is now on [![Discord server](https://img.shields.io/discord/752822148795596940?color=blue&label=discord&logo=discord)](https://discord.gg/Z4ujTwE) - +ESP3D is now on discord https://discord.gg/Z4ujTwE diff --git a/config/pack.js b/config/pack.js new file mode 100644 index 00000000..46b4f2f2 --- /dev/null +++ b/config/pack.js @@ -0,0 +1,82 @@ +/* + package.js - ESP3D WebUI package file + + Copyright (c) 2021 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +const chalk = require("chalk"); +const path = require("path"); +const fs = require("fs"); +const { Compress } = require("gzipper"); +const minify = require("html-minifier").minify; +const { exit } = require("process"); +console.log(chalk.cyan("Running Package 1.0")); +//Sanity check for command line +const arg = process.argv[process.argv.length > 1 ? 2 : 0]; +if (process.argv.length < 3 || !arg.startsWith("target=")) { + console.log( + chalk.red("Error!\nSyntax: npm run package target=") + ); + exit(0); + ml; +} +//Sanity check for file creation +const sourcepath = path.normalize( + __dirname + "/../" + arg.replace("target=", "") +); +const sourcedir = path.dirname(sourcepath); +console.log("Checking ", sourcepath); +if (!fs.existsSync(sourcepath)) { + console.log(chalk.red("Error!\nCannot find file")); + exit(0); +} +//Start minify +console.log(chalk.green("Processing")); +const source = fs.readFileSync(sourcepath, "UTF-8"); +const result = minify(source, { + removeComments: true, + collapseWhitespace: true, + minifyJS: true, + minifyCSS: true, +}); +const minifiedFile = sourcepath + ".minified.html"; +fs.writeFileSync(minifiedFile, result); +//Compress minified file +const compressedDir = sourcepath; +console.log( + chalk.blue("Minify: " + source.length + " Bytes => ", result.length, "Bytes") +); +let gzipper = new Compress(minifiedFile, sourcedir, { verbose: true }); +gzipper + .run() + .then((files) => { + //remove temp minified file + fs.unlinkSync(minifiedFile); + //rename compressed file to match original filename + fs.renameSync(minifiedFile + ".gz", sourcepath + ".gz"); + const finalSize = fs.lstatSync(sourcepath + ".gz").size; + //Show report + console.log( + chalk.green( + "\n\nMinify + Compression: ", + source.length, + "Bytes =>", + finalSize, + "Bytes = compression ", + (100 - 100 * (finalSize / source.length)).toFixed(2), + "%\n" + ) + ); + }) + .catch((err) => console.error(err)); diff --git a/config/server.js b/config/server.js new file mode 100644 index 00000000..b890b014 --- /dev/null +++ b/config/server.js @@ -0,0 +1,322 @@ +const express = require("express") +const expressStaticGzip = require("express-static-gzip") +const chalk = require("chalk") +let path = require("path") +const fs = require("fs") +const port = 8080 +/* + * Web Server for development + * Web Socket server for development + */ +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const WebSocket = require("ws") +let currentID = 0 +const app = express() +const fileUpload = require("express-fileupload") +let sensorInterval = 0 + +//const serverpath = path.normalize(__dirname + "/../server/public/"); + +const subtarget = process.env.SUBTARGET_ENV + ? process.env.SUBTARGET_ENV + : "Marlin" +const target = process.env.TARGET_ENV ? process.env.TARGET_ENV : "Printer3D" + +const serverpath = + path.normalize(__dirname + "/../server/" + target + "/" + subtarget) + "/" +if (!fs.existsSync(serverpath + "Flash")) { + fs.mkdirSync(serverpath + "Flash", { recursive: true }) +} +if (!fs.existsSync(serverpath + "SD")) { + fs.mkdirSync(serverpath + "SD", { recursive: true }) +} + +const { + commandsQuery, + configURI, + getLastconnection, + hasEnabledAuthentication, +} = require(path.normalize( + __dirname + "/targets/" + target + "/" + subtarget + "/index.js" +)) + +const WebSocketServer = require("ws").Server, + wss = new WebSocketServer({ port: 81 }) +app.use("/", express.static(serverpath + "Flash")) +app.use("/sd", express.static(serverpath + "sd")) +app.use("/", expressStaticGzip(serverpath + "Flash")) +app.use(fileUpload({ preserveExtension: true, debug: false })) + +app.listen(port, () => console.log("Env:", subtarget, ":", target)) +app.timeout = 2000 + +//app.use(express.urlencoded({ extended: false })); + +function SendWS(text, isbinary = true, isNotification = true) { + if (typeof isbinary === "undefined") isbinary = true + if (isbinary) { + const array = new Uint8Array(text.length) + for (let i = 0; i < array.length; ++i) { + array[i] = text.charCodeAt(i) + } + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(array) + } + }) + } else { + const notif = (isNotification ? "NOTIFICATION:" : "") + text + console.log(wscolor("[ws] send:", notif)) + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(notif) + } + }) + } +} + +app.post("/login", function (req, res) { + loginURI(req, res) +}) + +app.get("/config", function (req, res) { + configURI(req, res) +}) + +app.get("/command", function (req, res) { + commandsQuery(req, res, SendWS) +}) + +/*app.get("/sdfiles", function (req, res) { + res.status(200); + res.send( + '{"files":[{"name":"LOST.DIR","shortname":"LOST.DIR","size":"-1"},{"name":".android_secure","shortname":"ANDROI~1","size":"-1"},{"name":"app","shortname":"APP","size":"-1"},{"name":"framework","shortname":"FRAMEW~1","size":"-1"},{"name":"lib","shortname":"LIB","size":"-1"},{"name":"permissions","shortname":"PERMIS~1","size":"-1"},{"name":".Trash-1000","shortname":"TRASH-~1","size":"-1"},{"name":".FileExpert","shortname":"FILEEX~1","size":"-1"},{"name":"download","shortname":"DOWNLOAD","size":"-1"},{"name":"Android","shortname":"ANDROID","size":"-1"},{"name":".mmsyscache","shortname":"MMSYSC~1","size":"-1"},{"name":"clockworkmod","shortname":"CLOCKW~1","size":"-1"},{"name":"Evernote","shortname":"EVERNOTE","size":"-1"},{"name":"data","shortname":"DATA","size":"-1"},{"name":".estrongs","shortname":"ESTRON~1","size":"-1"},{"name":"DCIM","shortname":"DCIM","size":"-1"},{"name":"backups","shortname":"BACKUPS","size":"-1"},{"name":".zdworks","shortname":"ZDWORK~1","size":"-1"},{"name":"toolbox-stericson","shortname":"TOOLBO~1","size":"75.54 KB"},{"name":"SystemROMToolbox","shortname":"SYSTEM~1","size":"-1"},{"name":"busybox-stericson","shortname":"BUSYBO~1","size":"843.20 KB"},{"name":"recovery.img","shortname":"RECOVERY.IMG","size":"6.00 MB"},{"name":"preloader.img","shortname":"PRELOA~1.IMG","size":"128.00 KB"},{"name":"nvram.img","shortname":"NVRAM.IMG","size":"3.00 MB"},{"name":"seccnfg.img","shortname":"SECCNFG.IMG","size":"128.00 KB"},{"name":"uboot.img","shortname":"UBOOT.IMG","size":"384.00 KB"},{"name":"boot.img","shortname":"BOOT.IMG","size":"6.00 MB"},{"name":"secstatic.img","shortname":"SECSTA~1.IMG","size":"1.12 MB"},{"name":"system.img","shortname":"SYSTEM.IMG","size":"170.00 MB"},{"name":"misc.img","shortname":"MISC.IMG","size":"384.00 KB"},{"name":"cache.img","shortname":"CACHE.IMG","size":"60.00 MB"},{"name":"logo.img","shortname":"LOGO.IMG","size":"3.00 MB"},{"name":"expdb.img","shortname":"EXPDB.IMG","size":"640.00 KB"},{"name":"userdata.img","shortname":"USERDATA.IMG","size":"261.25 MB"},{"name":"recovery2.img","shortname":"RECOVE~1.IMG","size":"6.00 MB"},{"name":"touchrec.img","shortname":"TOUCHREC.IMG","size":"4.00 MB"},{"name":"restart.gcode","shortname":"RESTAR~1.GCO","size":"1 B"},{"name":"M3.G","shortname":"M3.G","size":"75 B"},{"name":"WeChat Image_20210108102318.jpg","shortname":"WECHAT~1.JPG","size":"876.47 KB"},{"name":"test.txt","shortname":"test.txt","size":"0 B"},{"name":"foo.txt","shortname":"foo.txt","size":"13 B"},{"name":"myfile.txt","shortname":"myfile.txt","size":"0 B"},{"name":"update.zip","shortname":"update.zip","size":"1.29 MB"},{"name":"luc","shortname":"luc","size":"-1"}],"path":"/","occupation":"22","status":"ok","total":"3.67 GB","used":"833.38 MB"}' + ); + console.log(commandcolor(`[server]/sdfiles)`)); + return; +});*/ + +function fileSizeString(size) { + if (size === -1) return "" + const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + let i = 0 + while (size >= 1024) { + size /= 1024 + ++i + } + return `${size.toFixed(2)} ${units[i]}` +} + +function filesList(mypath, destination) { + const currentPath = path.normalize(serverpath + destination + mypath) + console.log("[path]" + currentPath) + const totalUsed = getTotalSize(serverpath + destination) + const total = (destination == "SD" ? 4096 : 1.31) * 1024 * 1024 + const occupation = ((100 * totalUsed) / total).toFixed(0) + + const files = fs.readdirSync(currentPath).map((file) => { + const fullpath = path.normalize(currentPath + "/" + file) + const fst = fs.statSync(fullpath) + const fsize = fst.isFile() ? fileSizeString(fst.size) : "-1" + return { name: file, size: fsize } + }) + + const response = { + files, + path: mypath, + occupation, + status: "ok", + total: fileSizeString(total), + used: fileSizeString(totalUsed), + } + + return JSON.stringify(response) +} + +const getAllFiles = function (dirPath, arrayOfFiles = []) { + let files = fs.readdirSync(dirPath) || [] + const newFiles = files.reduce((acc, file) => { + const fullpath = dirPath + "/" + file + return fs.statSync(fullpath).isDirectory() + ? getAllFiles(fullpath, acc) + : [...acc, fullpath] + }, []) + return [...arrayOfFiles, ...newFiles] +} + +const getTotalSize = function (directoryPath) { + const allFiles = getAllFiles(directoryPath) + console.log("allFiles", allFiles) + return allFiles.reduce( + (acc, currFile) => acc + fs.statSync(currFile).size, + 0 + ) +} + +function deleteFolderRecursive(path) { + if (fs.existsSync(path) && fs.lstatSync(path).isDirectory()) { + fs.readdirSync(path).forEach(function (file, index) { + let curPath = path + "/" + file + + if (fs.lstatSync(curPath).isDirectory()) { + // recurse + deleteFolderRecursive(curPath) + } else { + // delete file + fs.unlinkSync(curPath) + } + }) + + console.log(`[server]Deleting directory "${path}"...`) + if (fs.existsSync(path)) fs.rmdirSync(path) + } else console.log(`[server]No directory "${path}"...`) +} + +app.all("/updatefw", function (req, res) { + res.send("ok") +}) + +app.all("/files", function (req, res) { + let mypath = req.query.path + let url = req.originalUrl + let filepath = path.normalize( + serverpath + "Flash" + mypath + "/" + req.query.filename + ) + if (url.indexOf("action=deletedir") != -1) { + console.log("[server]delete directory " + filepath) + deleteFolderRecursive(filepath) + fs.readdirSync(mypath) + } else if (url.indexOf("action=delete") != -1) { + console.log("[server]delete file " + filepath) + fs.unlinkSync(filepath) + } + if (url.indexOf("action=createdir") != -1) { + fs.mkdirSync(filepath) + console.log("[server]new directory " + filepath) + } + if (typeof mypath == "undefined") { + if (typeof req.body.path == "undefined") { + console.log("[server]path is not defined") + mypath = "/" + } else { + mypath = (req.body.path == "/" ? "" : req.body.path) + "/" + } + } + console.log("[server]path is " + mypath) + if (!req.files || Object.keys(req.files).length === 0) { + return res.send(filesList(mypath, "Flash")) + } + let myFile = req.files.myfiles + if (typeof myFile.length == "undefined") { + let fullpath = path.normalize( + serverpath + "Flash" + mypath + myFile.name + ) + console.log("[server]one file:" + fullpath) + myFile.mv(fullpath, function (err) { + if (err) return res.status(500).send(err) + res.send(filesList(mypath, "Flash")) + }) + return + } else { + console.log(myFile.length + " files") + for (let i = 0; i < myFile.length; i++) { + let fullpath = path.normalize( + serverpath + "Flash" + mypath + myFile[i].name + ) + console.log(fullpath) + myFile[i].mv(fullpath).then(() => { + if (i == myFile.length - 1) res.send(filesList(mypath, "Flash")) + }) + } + } +}) + +app.all("/sdfiles", function (req, res) { + let mypath = req.query.path + let url = req.originalUrl + let filepath = path.normalize( + serverpath + "SD" + mypath + "/" + req.query.filename + ) + if (url.indexOf("action=deletedir") != -1) { + console.log("[server]delete directory " + filepath) + deleteFolderRecursive(filepath) + fs.readdirSync(mypath) + } else if (url.indexOf("action=delete") != -1) { + fs.unlinkSync(filepath) + console.log("[server]delete file " + filepath) + } + if (url.indexOf("action=createdir") != -1) { + fs.mkdirSync(filepath) + console.log("[server]new directory " + filepath) + } + if (typeof mypath == "undefined") { + if (typeof req.body.path == "undefined") { + console.log("[server]path is not defined") + mypath = "/" + } else { + mypath = (req.body.path == "/" ? "" : req.body.path) + "/" + } + } + console.log("[server]path is " + mypath) + if (!req.files || Object.keys(req.files).length === 0) { + return res.send(filesList(mypath, "SD")) + } + let myFile = req.files.myfiles + if (typeof myFile.length == "undefined") { + let fullpath = path.normalize(serverpath + "SD" + mypath + myFile.name) + console.log("[server]one file:" + fullpath) + myFile.mv(fullpath, function (err) { + if (err) return res.status(500).send(err) + res.send(filesList(mypath, "SD")) + }) + return + } else { + console.log(myFile.length + " files") + for (let i = 0; i < myFile.length; i++) { + let fullpath = path.normalize( + serverpath + "SD" + mypath + myFile[i].name + ) + console.log(fullpath) + myFile[i].mv(fullpath).then(() => { + if (i == myFile.length - 1) res.send(filesList(mypath, "SD")) + }) + } + } +}) + +wss.on("connection", (socket, request) => { + console.log(wscolor("[ws] New connection")) + console.log(wscolor(`[ws] currentID:${currentID}`)) + socket.send(`currentID:${currentID}`) + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(`activeID:${currentID}`) + } + }) + clearInterval(sensorInterval) + sensorInterval = setInterval(() => { + const sensorTxt = "SENSOR:10[C] 15[%]" + SendWS(sensorTxt, false, false) + }, 3000) + currentID++ + socket.on("message", (message) => { + console.log(wscolor("[ws] received:", message)) + if (hasEnabledAuthentication() && message.startsWith("PING:")) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + let t = hasEnableAuthentication() + ? sessiontTime - (Date.now() - getLastconnection()) + : 60000 + let remainingtime = t < 0 ? 0 : t + console.log("remain:", remainingtime, "millisec") + client.send(`PING:${remainingtime}:60000`) + } + }) + } + }) +}) +wss.on("error", (error) => { + console.log(wscolor("[ws] Error:", error)) +}) diff --git a/config/targets/CNC/FluidNC/index.js b/config/targets/CNC/FluidNC/index.js new file mode 100644 index 00000000..14eda7f3 --- /dev/null +++ b/config/targets/CNC/FluidNC/index.js @@ -0,0 +1,669 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk"); +const wscolor = chalk.cyan; +const expresscolor = chalk.green; +const commandcolor = chalk.white; +const enableAuthentication = false; +let lastconnection = Date.now(); +let logindone = false; +const sessiontTime = 60000; +let countStatus = 0; + +function getLastconnection() { + return lastconnection; +} + +function hasEnabledAuthentication() { + return enableAuthentication; +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl; + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)); + else console.log(commandcolor(`[server]/command : ${url}`)); + if (url.indexOf("PING") != -1) { + lastconnection = Date.now(); + res.status(200); + res.send("ok\n"); + console.log(commandcolor(`[server]/command :PING`)); + return; + } + + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + if (url.indexOf("$CD") != -1 && url.indexOf("$CD=") == -1) { + SendWS( + "[MSG: BeginData]\n" + + "board: Bart Board\n" + + "name: Default (Test Drive with SD)\n" + + "stepping:\n" + + " engine: RMT\n" + + " idle_ms: 255\n" + + " pulse_us: 4\n" + + " dir_delay_us: 0\n" + + " disable_delay_us: 0\n" + + "\n" + + "axes:\n" + + " shared_stepper_disable: NO_PIN\n" + + " x:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + " y:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + " z:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + "spi:\n" + + " miso: gpio.19\n" + + " mosi: gpio.23\n" + + " sck: gpio.18\n" + + "\n" + + "sdcard:\n" + + " cs: gpio.5\n" + + " card_detect: NO_PIN\n" + + "\n" + + "control:\n" + + " safety_door: NO_PIN\n" + + " reset: NO_PIN\n" + + " feed_hold: NO_PIN\n" + + " cycle_start: NO_PIN\n" + + " macro0: NO_PIN\n" + + " macro1: NO_PIN\n" + + " macro2: NO_PIN\n" + + " macro3: NO_PIN\n" + + "\n" + + "coolant:\n" + + " flood: NO_PIN\n" + + " mist: NO_PIN\n" + + " delay_ms: 0\n" + + "\n" + + "probe:\n" + + " pin: NO_PIN\n" + + " check_mode_start: true\n" + + "\n" + + "macros:\n" + + " n0: \n" + + " n1: \n" + + " macro0: \n" + + " macro1: \n" + + " macro2: \n" + + " macro3: \n" + + "\n" + + "user_outputs:\n" + + " analog0: NO_PIN\n" + + " analog1: NO_PIN\n" + + " analog2: NO_PIN\n" + + " analog3: NO_PIN\n" + + " analog_frequency0: 5000\n" + + " analog_frequency1: 5000\n" + + " analog_frequency2: 5000\n" + + " analog_frequency3: 5000\n" + + " digital0: NO_PIN\n" + + " digital1: NO_PIN\n" + + " digital2: NO_PIN\n" + + " digital3: NO_PIN\n" + + "\n" + + "software_debounce_ms: 0\n" + + "laser_mode: false\n" + + "arc_tolerance: 0.002\n" + + "junction_deviation: 0.010\n" + + "verbose_errors: false\n" + + "report_inches: false\n" + + "homing_init_lock: true\n" + + "enable_parking_override_control: false\n" + + "deactivate_parking_upon_init: false\n" + + "check_limits_at_init: true\n" + + "limits_two_switches_on_axis: false\n" + + "disable_laser_during_hold: true\n" + + "use_line_numbers: false\n" + + "NoSpindle:\n" + + "\n" + + "[MSG: EndData]\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("$Config/Filename") != -1) { + if (url.indexOf("$Config/Filename=") == -1) + SendWS("$Config/Filename=config.yaml\n"); + SendWS("ok\n"); + res.send(""); + return; + } + if (req.query.cmd && req.query.cmd == "?") { + countStatus++; + if (countStatus == 1) + SendWS( + "\n" + ); + if (countStatus == 2) + SendWS( + "\n" + ); + if (countStatus > 2) + SendWS("\n"); + if (countStatus == 10) countStatus = 0; + res.send(""); + return; + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "fluidnc", + FWTargetID: "60", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + }, + }); + return; + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111"); + return; + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "fluidnc" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }); + return; + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }); + return; + } + + if (url.indexOf("$$") != -1) { + SendWS( + "$0=3\n" + + "$1=250\n" + + "$2=0\n" + + "$3=0\n" + + "$4=0\n" + + "$5=1\n" + + "$6=0\n" + + "$10=1\n" + + "$11=0.010\n" + + "$12=0.002\n" + + "$13=0\n" + + "$20=0\n" + + "$21=0\n" + + "$22=0\n" + + "$23=3\n" + + "$24=200.000\n" + + "$25=2000.000\n" + + "$26=250\n" + + "$27=1.000\n" + + "$30=1000.000\n" + + "$31=0.000\n" + + "$32=0\n" + + "$100=100.000\n" + + "$101=100.000\n" + + "$102=100.000\n" + + "$103=100.000\n" + + "$104=100.000\n" + + "$105=100.000\n" + + "$110=1000.000\n" + + "$111=1000.000\n" + + "$112=1000.000\n" + + "$113=1000.000\n" + + "$114=1000.000\n" + + "$115=1000.000\n" + + "$120=200.000\n" + + "$121=200.000\n" + + "$122=200.000\n" + + "$123=200.000\n" + + "$124=200.000\n" + + "$125=200.000\n" + + "$130=300.000\n" + + "$131=300.000\n" + + "$132=300.000\n" + + "$133=300.000\n" + + "$134=300.000\n" + + "$135=300.000\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8); + SendWS(text, false); + return; + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [{ none: "0" }, { pushover: "1" }, { email: "2" }, { line: "3" }], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }); + return; + } + SendWS("ok\n"); + res.send(""); +}; + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401); + logindone = false; + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true; + lastconnection = Date.now(); + } else { + res.status(401); + logindone = false; + } + res.send(""); +}; + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: FluidNC v3.2.2
" + + "FW arch: ESP32 " + ); +}; + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +}; diff --git a/config/targets/CNC/GRBL/index.js b/config/targets/CNC/GRBL/index.js new file mode 100644 index 00000000..7410d10b --- /dev/null +++ b/config/targets/CNC/GRBL/index.js @@ -0,0 +1,572 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk"); +const wscolor = chalk.cyan; +const expresscolor = chalk.green; +const commandcolor = chalk.white; +const enableAuthentication = false; +let lastconnection = Date.now(); +let logindone = false; +const sessiontTime = 60000; +let countStatus = 0; + +function getLastconnection() { + return lastconnection; +} + +function hasEnabledAuthentication() { + return enableAuthentication; +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl; + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)); + else console.log(commandcolor(`[server]/command : ${url}`)); + if (url.indexOf("PING") != -1) { + lastconnection = Date.now(); + res.status(200); + res.send("ok\n"); + console.log(commandcolor(`[server]/command :PING`)); + return; + } + + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + + if (req.query.cmd && req.query.cmd == "?") { + countStatus++; + if (countStatus == 1) + SendWS( + "\n" + ); + if (countStatus == 2) + SendWS( + "\n" + ); + if (countStatus > 2) + SendWS("\n"); + if (countStatus == 10) countStatus = 0; + res.send(""); + return; + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "grbl", + FWTargetID: "10", + Setup: "Enabled", + SDConnection: "none", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }); + return; + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111"); + return; + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "targetfw", value: "grbl" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }); + return; + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }); + return; + } + + if (url.indexOf("$$") != -1) { + SendWS( + "$0=3\n" + + "$1=250\n" + + "$2=0\n" + + "$3=0\n" + + "$4=0\n" + + "$5=1\n" + + "$6=0\n" + + "$10=1\n" + + "$11=0.010\n" + + "$12=0.002\n" + + "$13=0\n" + + "$20=0\n" + + "$21=0\n" + + "$22=0\n" + + "$23=3\n" + + "$24=200.000\n" + + "$25=2000.000\n" + + "$26=250\n" + + "$27=1.000\n" + + "$30=1000.000\n" + + "$31=0.000\n" + + "$32=0\n" + + "$100=100.000\n" + + "$101=100.000\n" + + "$102=100.000\n" + + "$103=100.000\n" + + "$104=100.000\n" + + "$105=100.000\n" + + "$110=1000.000\n" + + "$111=1000.000\n" + + "$112=1000.000\n" + + "$113=1000.000\n" + + "$114=1000.000\n" + + "$115=1000.000\n" + + "$120=200.000\n" + + "$121=200.000\n" + + "$122=200.000\n" + + "$123=200.000\n" + + "$124=200.000\n" + + "$125=200.000\n" + + "$130=300.000\n" + + "$131=300.000\n" + + "$132=300.000\n" + + "$133=300.000\n" + + "$134=300.000\n" + + "$135=300.000\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8); + SendWS(text, false); + return; + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [{ none: "0" }, { pushover: "1" }, { email: "2" }, { line: "3" }], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "10", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }); + return; + } + SendWS("ok\n"); + res.send(""); +}; + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401); + logindone = false; + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true; + lastconnection = Date.now(); + } else { + res.status(401); + logindone = false; + } + res.send(""); +}; + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: grbl
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ); +}; + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +}; diff --git a/config/targets/Printer3D/Marlin-embedded/index.js b/config/targets/Printer3D/Marlin-embedded/index.js new file mode 100644 index 00000000..0fcecf6b --- /dev/null +++ b/config/targets/Printer3D/Marlin-embedded/index.js @@ -0,0 +1,843 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +const SERIAL_PROTOCOL = "RAW" +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 +const roomTemperature = 20 +const temperatures = { + T: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + { + value: 20, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + B: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.2, + coolspeed: 0.8, + variation: 0.5, + }, + ], + C: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + P: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 0.5 }], //let say probe is 50% of extruder temperature + R: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 1 }], //the redondant is same as extruder 0 + M: [{ value: roomTemperature, lastTime: -1, variation: 1 }], //the motherboard is same as room temperature +5/10 degres +} + +const updateTemperature = (entry, time) => { + if (entry.lastTime == -1) { + entry.lastTime = time + } + + const v = Math.random() * 5 + //heater + if (typeof entry.target != "undefined") { + const target = entry.target == 0 ? roomTemperature : entry.target + if (entry.value + 5 < target) { + entry.value = + entry.value + (entry.heatspeed * (time - entry.lastTime)) / 1000 + } else if (entry.value - 5 > target) { + entry.value = + entry.value - (entry.coolspeed * (time - entry.lastTime)) / 1000 + } else if (target - 2 < entry.value && entry.value < target + 2) { + entry.value = target + entry.variation * (Math.random() - 0.5) + } else if (entry.value < target) { + entry.value = + entry.value + + ((entry.heatspeed / 3) * (time - entry.lastTime)) / 1000 + } else { + entry.value = + entry.value - + ((entry.coolspeed / 3) * (time - entry.lastTime)) / 1000 + } + } + //sensor + else if (typeof entry.sameas != "undefined") { + if ( + entry.sameas == "T" && + entry.variation == 0.5 && + entry.value < 2 * roomTemperature + ) { + //probe is same as room temperature if under 40 degres + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } else { + entry.value = + v > 2.5 + ? temperatures[entry.sameas][0].value * entry.variation + + v / 4 + : temperatures[entry.sameas][0].value * entry.variation - + v / 4 + } + } else { + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } + entry.lastTime = time +} + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + Object.keys(temperatures).map((tool) => { + temperatures[tool].map((entry) => { + updateTemperature(entry, new Date()) + }) + }) + const result = + "ok T:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " C:" + + Number(temperatures["C"][0].value).toFixed(2) + + " /" + + Number(temperatures["C"][0].target).toFixed(2) + + " B:" + + Number(temperatures["B"][0].value).toFixed(2) + + " /" + + Number(temperatures["B"][0].target).toFixed(2) + + " P:" + + Number(temperatures["P"][0].value).toFixed(2) + + " /0 R:" + + Number(temperatures["R"][0].value).toFixed(2) + + " /0 T0:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " T1:" + + Number(temperatures["T"][1].value).toFixed(2) + + " /" + + Number(temperatures["T"][1].target).toFixed(2) + + " @:0\n" + console.log(result) + return result +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.00 Count X: 0 Y:10160 Z:116000\nok\n`) + res.send("") + return + } + if (url.indexOf("SIM:") != -1) { + const response = url.substring(url.indexOf("SIM:") + 4) + SendWS(response + "\n" + "ok\n") + res.send("") + return + } + + if (url.indexOf("M20 1:") != -1) { + SendWS( + "Begin file list\n" + + "System Volume Information.DIR\n" + + "mycode3.gco\n" + + "CUBE.GCO\n" + + "bak_pic.DIR\n" + + "bak_font.DIR\n" + + "macro1.g\n" + + "BAK.DIR\n" + + "End file list\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M20 L") != -1) { + SendWS( + "echo:SD card ok\n" + + "ok\n" + + "Begin file listt\n" + + "V2T-TEST.GCO 569266 V2T-TEST.GCO\n" + + "DFQ-PI~1.GCO 1490254 DFq-pika2.gco\n" + + "VJ1-TEST.GCO 569266 VJ1-TEST.GCO\n" + + "XRP-SU~1.GCO 569266 xRP-SupportChain-Body.gcod\n" + + "RGW-PI~1.GCO 1490254 rgw-pika2.gco\n" + + "PIKA2~1.GCO 1635116 pika2.gcode\n" + + "UJP-PI~1.GCO 1573255 ujp-pika2.gcode\n" + + "CTEST/INDEXH~1.GZ 69111 /index.html.gz\n" + + "PIKA2.GCO 1635116 PIKA2.GCO\n" + + "MACRO1.GCO 19 MACRO1.GCO\n" + + "INDEX_~1.GZ 78055 index.html.gz\n" + + "GCODE/TESTLO~1.GCO 25 /testlongname.gcode\n" + + "GCODE/TESTCUBE.GCO 353194 /TESTCUBE.GCO\n" + + "RESOUR~1/GCODE/APPLE~1.GCO 8140 resources//Apple.gcode\n" + + "RESOUR~1/GCODE/BANANA~1.GCO 7554 resources//Banana.gcode\n" + + "RESOUR~1/GCODE/CHERRY~1.GCO 6465 resources//Cherry.gcode\n" + + "RESOUR~1/GCODE/PEACH~1.GCO 8467 resources//Peach.gcode\n" + + "RESOUR~1/GCODE/PEAR~1.GCO 6010 resources//Pear.gcode\n" + + "TESTCUBE.GCO 353194 TESTCUBE.GCO\n" + + "TEST1.GCO 1143935 TEST1.GCO\n" + + "End file list\n" + + "ok" + ) + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "echo:SD card ok\n" + + "ok\n" + + "Begin file list\n" + + "V2T-TEST.GCO 569266\n" + + "DFQ-PI~1.GCO 1490254\n" + + "VJ1-TEST.GCO 569266\n" + + "XRP-SU~1.GCO 569266\n" + + "RGW-PI~1.GCO 1490254\n" + + "PIKA2~1.GCO 1635116\n" + + "UJP-PI~1.GCO 1573255\n" + + "CTEST/INDEXH~1.GZ 69111\n" + + "PIKA2.GCO 1635116\n" + + "MACRO1.GCO 19\n" + + "INDEX_~1.GZ 78055\n" + + "GCODE/TESTLO~1.GCO 25\n" + + "GCODE/TESTCUBE.GCO 353194\n" + + "RESOUR~1/GCODE/APPLE~1.GCO 8140\n" + + "RESOUR~1/GCODE/BANANA~1.GCO 7554\n" + + "RESOUR~1/GCODE/CHERRY~1.GCO 6465\n" + + "RESOUR~1/GCODE/PEACH~1.GCO 8467\n" + + "RESOUR~1/GCODE/PEAR~1.GCO 6010\n" + + "TESTCUBE.GCO 353194\n" + + "118.GCO 41\n" + + "TEST1.GCO 1143935\n" + + "End file list\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M104") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const reg_ex_index = /T([0-9])/ + const result_target = reg_ex_temp.exec(url) + const result_index = reg_ex_index.exec(url) + console.log(result_target[1], result_index[1]) + temperatures["T"][result_index[1]].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M140") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["B"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M141") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["C"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Marlin 2.0.9.1 (Sep 8 2021 17:07:06) SOURCE_CODE_URL:github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:MRR ESPA EXTRUDER_COUNT:1 UUID:cede2a2f-41a2-4748-9b12-c55c62f367ff\n" + + "Cap:SERIAL_XON_XOFF:0\n" + + "Cap:BINARY_FILE_TRANSFER:0\n" + + "Cap:EEPROM:0\n" + + "Cap:VOLUMETRIC:1\n" + + "Cap:AUTOREPORT_POS:0\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:PROGRESS:0\n" + + "Cap:PRINT_JOB:1\n" + + "Cap:AUTOLEVEL:0\n" + + "Cap:RUNOUT:0\n" + + "Cap:Z_PROBE:0\n" + + "Cap:LEVELING_DATA:0\n" + + "Cap:BUILD_PERCENT:0\n" + + "Cap:SOFTWARE_POWER:0\n" + + "Cap:TOGGLE_LIGHTS:0\n" + + "Cap:CASE_LIGHT_BRIGHTNESS:0\n" + + "Cap:EMERGENCY_PARSER:0\n" + + "Cap:HOST_ACTION_COMMANDS:0\n" + + "Cap:PROMPT_SUPPORT:0\n" + + "Cap:SDCARD:1\n" + + "Cap:REPEAT:0\n" + + "Cap:SD_WRITE:1\n" + + "Cap:AUTOREPORT_SD_STATUS:0\n" + + "Cap:LONG_FILENAME:1\n" + + "Cap:THERMAL_PROTECTION:1\n" + + "Cap:MOTION_MODES:0\n" + + "Cap:ARCS:1\n" + + "Cap:BABYSTEPPING:0\n" + + "Cap:CHAMBER_TEMPERATURE:0\n" + + "Cap:COOLER_TEMPERATURE:0\n" + + "Cap:MEATPACK:0\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M503") != -1) { + SendWS( + "echo:; Linear Units:\n" + + "echo: G21 ; (mm)\n" + + "echo:; Temperature Units:\n" + + "echo: M149 C ; Units in Celsius\n" + + "echo:; Filament settings (Disabled):\n" + + "echo: M200 S0 D1.75\n" + + "echo:; Steps per unit:\n" + + "echo: M92 X80.00 Y80.00 Z400.00 E500.00\n" + + "echo:; Max feedrates (units/s):\n" + + "echo: M203 X300.00 Y300.00 Z5.00 E25.00\n" + + "echo:; Max Acceleration (units/s2):\n" + + "echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00\n" + + "echo:; Acceleration (units/s2) (P R T):\n" + + "echo: M204 P3000.00 R3000.00 T3000.00\n" + + "echo:; Advanced (B S T J):\n" + + "echo: M205 B20000.00 S0.00 T0.00 J0.01\n" + + "echo:; Home offset:\n" + + "echo: M206 X0.00 Y0.00 Z0.00\n" + + "echo:; Hotend PID:\n" + + "echo: M301 P22.20 I1.08 D114.00\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "2.0.9.3+-3.0.0.a111", + FWTarget: "marlin", + FWTargetID: "30", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none" + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "marlin" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: marlin
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/Printer3D/Marlin/index.js b/config/targets/Printer3D/Marlin/index.js new file mode 100644 index 00000000..96f82caf --- /dev/null +++ b/config/targets/Printer3D/Marlin/index.js @@ -0,0 +1,845 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +const SERIAL_PROTOCOL = "RAW" +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 +const roomTemperature = 20 +const temperatures = { + T: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + { + value: 20, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + B: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.2, + coolspeed: 0.8, + variation: 0.5, + }, + ], + C: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + P: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 0.5 }], //let say probe is 50% of extruder temperature + R: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 1 }], //the redondant is same as extruder 0 + M: [{ value: roomTemperature, lastTime: -1, variation: 1 }], //the motherboard is same as room temperature +5/10 degres +} + +const updateTemperature = (entry, time) => { + if (entry.lastTime == -1) { + entry.lastTime = time + } + + const v = Math.random() * 5 + //heater + if (typeof entry.target != "undefined") { + const target = entry.target == 0 ? roomTemperature : entry.target + if (entry.value + 5 < target) { + entry.value = + entry.value + (entry.heatspeed * (time - entry.lastTime)) / 1000 + } else if (entry.value - 5 > target) { + entry.value = + entry.value - (entry.coolspeed * (time - entry.lastTime)) / 1000 + } else if (target - 2 < entry.value && entry.value < target + 2) { + entry.value = target + entry.variation * (Math.random() - 0.5) + } else if (entry.value < target) { + entry.value = + entry.value + + ((entry.heatspeed / 3) * (time - entry.lastTime)) / 1000 + } else { + entry.value = + entry.value - + ((entry.coolspeed / 3) * (time - entry.lastTime)) / 1000 + } + } + //sensor + else if (typeof entry.sameas != "undefined") { + if ( + entry.sameas == "T" && + entry.variation == 0.5 && + entry.value < 2 * roomTemperature + ) { + //probe is same as room temperature if under 40 degres + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } else { + entry.value = + v > 2.5 + ? temperatures[entry.sameas][0].value * entry.variation + + v / 4 + : temperatures[entry.sameas][0].value * entry.variation - + v / 4 + } + } else { + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } + entry.lastTime = time +} + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + Object.keys(temperatures).map((tool) => { + temperatures[tool].map((entry) => { + updateTemperature(entry, new Date()) + }) + }) + const result = + "ok T:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " C:" + + Number(temperatures["C"][0].value).toFixed(2) + + " /" + + Number(temperatures["C"][0].target).toFixed(2) + + " B:" + + Number(temperatures["B"][0].value).toFixed(2) + + " /" + + Number(temperatures["B"][0].target).toFixed(2) + + " P:" + + Number(temperatures["P"][0].value).toFixed(2) + + " /0 R:" + + Number(temperatures["R"][0].value).toFixed(2) + + " /0 T0:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " T1:" + + Number(temperatures["T"][1].value).toFixed(2) + + " /" + + Number(temperatures["T"][1].target).toFixed(2) + + " @:0\n" + console.log(result) + return result +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.00 Count X: 0 Y:10160 Z:116000\nok\n`) + res.send("") + return + } + if (url.indexOf("SIM:") != -1) { + const response = url.substring(url.indexOf("SIM:") + 4) + SendWS(response + "\n" + "ok\n") + res.send("") + return + } + + if (url.indexOf("M20 1:") != -1) { + SendWS( + "Begin file list\n" + + "System Volume Information.DIR\n" + + "mycode3.gco\n" + + "CUBE.GCO\n" + + "bak_pic.DIR\n" + + "bak_font.DIR\n" + + "macro1.g\n" + + "BAK.DIR\n" + + "End file list\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M20 L") != -1) { + SendWS( + "echo:SD card ok\n" + + "ok\n" + + "Begin file listt\n" + + "V2T-TEST.GCO 569266 V2T-TEST.GCO\n" + + "DFQ-PI~1.GCO 1490254 DFq-pika2.gco\n" + + "VJ1-TEST.GCO 569266 VJ1-TEST.GCO\n" + + "XRP-SU~1.GCO 569266 xRP-SupportChain-Body.gcod\n" + + "RGW-PI~1.GCO 1490254 rgw-pika2.gco\n" + + "PIKA2~1.GCO 1635116 pika2.gcode\n" + + "UJP-PI~1.GCO 1573255 ujp-pika2.gcode\n" + + "CTEST/INDEXH~1.GZ 69111 /index.html.gz\n" + + "PIKA2.GCO 1635116 PIKA2.GCO\n" + + "MACRO1.GCO 19 MACRO1.GCO\n" + + "INDEX_~1.GZ 78055 index.html.gz\n" + + "GCODE/TESTLO~1.GCO 25 /testlongname.gcode\n" + + "GCODE/TESTCUBE.GCO 353194 /TESTCUBE.GCO\n" + + "RESOUR~1/GCODE/APPLE~1.GCO 8140 resources//Apple.gcode\n" + + "RESOUR~1/GCODE/BANANA~1.GCO 7554 resources//Banana.gcode\n" + + "RESOUR~1/GCODE/CHERRY~1.GCO 6465 resources//Cherry.gcode\n" + + "RESOUR~1/GCODE/PEACH~1.GCO 8467 resources//Peach.gcode\n" + + "RESOUR~1/GCODE/PEAR~1.GCO 6010 resources//Pear.gcode\n" + + "TESTCUBE.GCO 353194 TESTCUBE.GCO\n" + + "TEST1.GCO 1143935 TEST1.GCO\n" + + "End file list\n" + + "ok" + ) + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "echo:SD card ok\n" + + "ok\n" + + "Begin file list\n" + + "V2T-TEST.GCO 569266\n" + + "DFQ-PI~1.GCO 1490254\n" + + "VJ1-TEST.GCO 569266\n" + + "XRP-SU~1.GCO 569266\n" + + "RGW-PI~1.GCO 1490254\n" + + "PIKA2~1.GCO 1635116\n" + + "UJP-PI~1.GCO 1573255\n" + + "CTEST/INDEXH~1.GZ 69111\n" + + "PIKA2.GCO 1635116\n" + + "MACRO1.GCO 19\n" + + "INDEX_~1.GZ 78055\n" + + "GCODE/TESTLO~1.GCO 25\n" + + "GCODE/TESTCUBE.GCO 353194\n" + + "RESOUR~1/GCODE/APPLE~1.GCO 8140\n" + + "RESOUR~1/GCODE/BANANA~1.GCO 7554\n" + + "RESOUR~1/GCODE/CHERRY~1.GCO 6465\n" + + "RESOUR~1/GCODE/PEACH~1.GCO 8467\n" + + "RESOUR~1/GCODE/PEAR~1.GCO 6010\n" + + "TESTCUBE.GCO 353194\n" + + "118.GCO 41\n" + + "TEST1.GCO 1143935\n" + + "End file list\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M104") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const reg_ex_index = /T([0-9])/ + const result_target = reg_ex_temp.exec(url) + const result_index = reg_ex_index.exec(url) + console.log(result_target[1], result_index[1]) + temperatures["T"][result_index[1]].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M140") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["B"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M141") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["C"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Marlin 2.0.9.1 (Sep 8 2021 17:07:06) SOURCE_CODE_URL:github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:MRR ESPA EXTRUDER_COUNT:1 UUID:cede2a2f-41a2-4748-9b12-c55c62f367ff\n" + + "Cap:SERIAL_XON_XOFF:0\n" + + "Cap:BINARY_FILE_TRANSFER:0\n" + + "Cap:EEPROM:0\n" + + "Cap:VOLUMETRIC:1\n" + + "Cap:AUTOREPORT_POS:0\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:PROGRESS:0\n" + + "Cap:PRINT_JOB:1\n" + + "Cap:AUTOLEVEL:0\n" + + "Cap:RUNOUT:0\n" + + "Cap:Z_PROBE:0\n" + + "Cap:LEVELING_DATA:0\n" + + "Cap:BUILD_PERCENT:0\n" + + "Cap:SOFTWARE_POWER:0\n" + + "Cap:TOGGLE_LIGHTS:0\n" + + "Cap:CASE_LIGHT_BRIGHTNESS:0\n" + + "Cap:EMERGENCY_PARSER:0\n" + + "Cap:HOST_ACTION_COMMANDS:0\n" + + "Cap:PROMPT_SUPPORT:0\n" + + "Cap:SDCARD:1\n" + + "Cap:REPEAT:0\n" + + "Cap:SD_WRITE:1\n" + + "Cap:AUTOREPORT_SD_STATUS:0\n" + + "Cap:LONG_FILENAME:1\n" + + "Cap:THERMAL_PROTECTION:1\n" + + "Cap:MOTION_MODES:0\n" + + "Cap:ARCS:1\n" + + "Cap:BABYSTEPPING:0\n" + + "Cap:CHAMBER_TEMPERATURE:0\n" + + "Cap:COOLER_TEMPERATURE:0\n" + + "Cap:MEATPACK:0\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M503") != -1) { + SendWS( + "echo:; Linear Units:\n" + + "echo: G21 ; (mm)\n" + + "echo:; Temperature Units:\n" + + "echo: M149 C ; Units in Celsius\n" + + "echo:; Filament settings (Disabled):\n" + + "echo: M200 S0 D1.75\n" + + "echo:; Steps per unit:\n" + + "echo: M92 X80.00 Y80.00 Z400.00 E500.00\n" + + "echo:; Max feedrates (units/s):\n" + + "echo: M203 X300.00 Y300.00 Z5.00 E25.00\n" + + "echo:; Max Acceleration (units/s2):\n" + + "echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00\n" + + "echo:; Acceleration (units/s2) (P R T):\n" + + "echo: M204 P3000.00 R3000.00 T3000.00\n" + + "echo:; Advanced (B S T J):\n" + + "echo: M205 B20000.00 S0.00 T0.00 J0.01\n" + + "echo:; Home offset:\n" + + "echo: M206 X0.00 Y0.00 Z0.00\n" + + "echo:; Hotend PID:\n" + + "echo: M301 P22.20 I1.08 D114.00\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "marlin", + FWTargetID: "40", + Setup: "Enabled", + SDConnection: "none", + SerialProtocol: SERIAL_PROTOCOL, + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "marlin" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: marlin
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/Printer3D/RepRap-embedded/index.js b/config/targets/Printer3D/RepRap-embedded/index.js new file mode 100644 index 00000000..2ec3aefd --- /dev/null +++ b/config/targets/Printer3D/RepRap-embedded/index.js @@ -0,0 +1,649 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + let T = Number(Math.floor(Math.random() * 215).toFixed(2)) + let T1 = Number(Math.floor(Math.random() * 215).toFixed(2)) + let B = Number(Math.floor(Math.random() * 45).toFixed(2)) + return ( + "ok T:" + + T + + " /200 C:" + + (T * 1.1).toFixed(2) + + " /200 B:" + + B + + " /0 P:" + + (B * 1.3).toFixed(2) + + " /0 R:" + + B * 2 + + " /0 T0:" + + T + + " /200 T1:" + + T1 + + " /0 @:0\n" + ) +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.00 Count X: 0 Y:10160 Z:116000\nok\n`) + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "Begin file list\n" + + "CUBE2.GCO 210240\n" + + "CUBE01.GCO 2089832\n" + + "SUPPORT2.GCO 4613256\n" + + "ARCHIVE/CUBE.GCO 210240\n" + + "ARCHIVE/CUBE-C~1.GCO 210240\n" + + "NEWFOL~1/SUPPORT2.GCO 4613256\n" + + "End file list\n" + + "ok\n" + ) + /* SendWS( + "Begin file list\n" + + "COOL_V~1.GCO 66622272\n" + + "415%VA~1.GCO 66622272\n" + + "/ARCHIEVE/SUBDIR/TWISTY~1.GCO 1040\n" + + "/ARCHIEVE/STEEL-~1.GCO 2040\n" + + "/ARCHIEVE/STEEL_~1.GCO 2040\n" + + "/ARCHIEVE/RET229~1.GCO 2050\n" + + "/ARCHIEVE/FILE__~1.GCO 1050\n" + + "/ARCHIEVE/FILE__~2.GCO 1050\n" + + "/ARCHIEVE/FILE__~3.GCO 1050\n" + + "/ARCHIEVE/FILE__~4.GCO 1050\n" + + "/ARCHIEVE/FILE__~5.GCO 1050\n" + + "End file list\n" + + "ok\n" + );*/ + res.send("") + return + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Marlin 2.0.9.1 (Sep 8 2021 17:07:06) SOURCE_CODE_URL:github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:MRR ESPA EXTRUDER_COUNT:1 UUID:cede2a2f-41a2-4748-9b12-c55c62f367ff\n" + + "Cap:SERIAL_XON_XOFF:0\n" + + "Cap:BINARY_FILE_TRANSFER:0\n" + + "Cap:EEPROM:0\n" + + "Cap:VOLUMETRIC:1\n" + + "Cap:AUTOREPORT_POS:0\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:PROGRESS:0\n" + + "Cap:PRINT_JOB:1\n" + + "Cap:AUTOLEVEL:0\n" + + "Cap:RUNOUT:0\n" + + "Cap:Z_PROBE:0\n" + + "Cap:LEVELING_DATA:0\n" + + "Cap:BUILD_PERCENT:0\n" + + "Cap:SOFTWARE_POWER:0\n" + + "Cap:TOGGLE_LIGHTS:0\n" + + "Cap:CASE_LIGHT_BRIGHTNESS:0\n" + + "Cap:EMERGENCY_PARSER:0\n" + + "Cap:HOST_ACTION_COMMANDS:0\n" + + "Cap:PROMPT_SUPPORT:0\n" + + "Cap:SDCARD:1\n" + + "Cap:REPEAT:0\n" + + "Cap:SD_WRITE:1\n" + + "Cap:AUTOREPORT_SD_STATUS:0\n" + + "Cap:LONG_FILENAME:1\n" + + "Cap:THERMAL_PROTECTION:1\n" + + "Cap:MOTION_MODES:0\n" + + "Cap:ARCS:1\n" + + "Cap:BABYSTEPPING:0\n" + + "Cap:CHAMBER_TEMPERATURE:0\n" + + "Cap:COOLER_TEMPERATURE:0\n" + + "Cap:MEATPACK:0\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M503") != -1) { + SendWS( + "echo: G21 ; Units in mm (mm)\n" + + " \n" + + "echo:; Filament settings: Disabled\n" + + "echo: M200 S0 D1.75\n" + + "echo:; Steps per unit:\n" + + "echo: M92 X80.00 Y80.00 Z400.00 E500.00\n" + + "echo:; Maximum feedrates (units/s):\n" + + "echo: M203 X300.00 Y300.00 Z5.00 E25.00\n" + + "echo:; Maximum Acceleration (units/s2):\n" + + "echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00\n" + + "echo:; Acceleration (units/s2): P R T\n" + + "echo: M204 P3000.00 R3000.00 T3000.00\n" + + "echo:; Advanced: B S T J\n" + + "echo: M205 B20000.00 S0.00 T0.00 J0.01\n" + + "echo:; Home offset:\n" + + "echo: M206 X0.00 Y0.00 Z0.00\n" + + "echo:; PID settings:\n" + + "echo: M301 P22.20 I1.08 D114.00\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "reprap", + FWTargetID: "70", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "reprap" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: reprap
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/Printer3D/RepRap/index.js b/config/targets/Printer3D/RepRap/index.js new file mode 100644 index 00000000..2ec3aefd --- /dev/null +++ b/config/targets/Printer3D/RepRap/index.js @@ -0,0 +1,649 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + let T = Number(Math.floor(Math.random() * 215).toFixed(2)) + let T1 = Number(Math.floor(Math.random() * 215).toFixed(2)) + let B = Number(Math.floor(Math.random() * 45).toFixed(2)) + return ( + "ok T:" + + T + + " /200 C:" + + (T * 1.1).toFixed(2) + + " /200 B:" + + B + + " /0 P:" + + (B * 1.3).toFixed(2) + + " /0 R:" + + B * 2 + + " /0 T0:" + + T + + " /200 T1:" + + T1 + + " /0 @:0\n" + ) +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.00 Count X: 0 Y:10160 Z:116000\nok\n`) + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "Begin file list\n" + + "CUBE2.GCO 210240\n" + + "CUBE01.GCO 2089832\n" + + "SUPPORT2.GCO 4613256\n" + + "ARCHIVE/CUBE.GCO 210240\n" + + "ARCHIVE/CUBE-C~1.GCO 210240\n" + + "NEWFOL~1/SUPPORT2.GCO 4613256\n" + + "End file list\n" + + "ok\n" + ) + /* SendWS( + "Begin file list\n" + + "COOL_V~1.GCO 66622272\n" + + "415%VA~1.GCO 66622272\n" + + "/ARCHIEVE/SUBDIR/TWISTY~1.GCO 1040\n" + + "/ARCHIEVE/STEEL-~1.GCO 2040\n" + + "/ARCHIEVE/STEEL_~1.GCO 2040\n" + + "/ARCHIEVE/RET229~1.GCO 2050\n" + + "/ARCHIEVE/FILE__~1.GCO 1050\n" + + "/ARCHIEVE/FILE__~2.GCO 1050\n" + + "/ARCHIEVE/FILE__~3.GCO 1050\n" + + "/ARCHIEVE/FILE__~4.GCO 1050\n" + + "/ARCHIEVE/FILE__~5.GCO 1050\n" + + "End file list\n" + + "ok\n" + );*/ + res.send("") + return + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Marlin 2.0.9.1 (Sep 8 2021 17:07:06) SOURCE_CODE_URL:github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:MRR ESPA EXTRUDER_COUNT:1 UUID:cede2a2f-41a2-4748-9b12-c55c62f367ff\n" + + "Cap:SERIAL_XON_XOFF:0\n" + + "Cap:BINARY_FILE_TRANSFER:0\n" + + "Cap:EEPROM:0\n" + + "Cap:VOLUMETRIC:1\n" + + "Cap:AUTOREPORT_POS:0\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:PROGRESS:0\n" + + "Cap:PRINT_JOB:1\n" + + "Cap:AUTOLEVEL:0\n" + + "Cap:RUNOUT:0\n" + + "Cap:Z_PROBE:0\n" + + "Cap:LEVELING_DATA:0\n" + + "Cap:BUILD_PERCENT:0\n" + + "Cap:SOFTWARE_POWER:0\n" + + "Cap:TOGGLE_LIGHTS:0\n" + + "Cap:CASE_LIGHT_BRIGHTNESS:0\n" + + "Cap:EMERGENCY_PARSER:0\n" + + "Cap:HOST_ACTION_COMMANDS:0\n" + + "Cap:PROMPT_SUPPORT:0\n" + + "Cap:SDCARD:1\n" + + "Cap:REPEAT:0\n" + + "Cap:SD_WRITE:1\n" + + "Cap:AUTOREPORT_SD_STATUS:0\n" + + "Cap:LONG_FILENAME:1\n" + + "Cap:THERMAL_PROTECTION:1\n" + + "Cap:MOTION_MODES:0\n" + + "Cap:ARCS:1\n" + + "Cap:BABYSTEPPING:0\n" + + "Cap:CHAMBER_TEMPERATURE:0\n" + + "Cap:COOLER_TEMPERATURE:0\n" + + "Cap:MEATPACK:0\n" + + "ok\n" + ) + res.send("") + return + } + if (url.indexOf("M503") != -1) { + SendWS( + "echo: G21 ; Units in mm (mm)\n" + + " \n" + + "echo:; Filament settings: Disabled\n" + + "echo: M200 S0 D1.75\n" + + "echo:; Steps per unit:\n" + + "echo: M92 X80.00 Y80.00 Z400.00 E500.00\n" + + "echo:; Maximum feedrates (units/s):\n" + + "echo: M203 X300.00 Y300.00 Z5.00 E25.00\n" + + "echo:; Maximum Acceleration (units/s2):\n" + + "echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00\n" + + "echo:; Acceleration (units/s2): P R T\n" + + "echo: M204 P3000.00 R3000.00 T3000.00\n" + + "echo:; Advanced: B S T J\n" + + "echo: M205 B20000.00 S0.00 T0.00 J0.01\n" + + "echo:; Home offset:\n" + + "echo: M206 X0.00 Y0.00 Z0.00\n" + + "echo:; PID settings:\n" + + "echo: M301 P22.20 I1.08 D114.00\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "reprap", + FWTargetID: "70", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "reprap" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: reprap
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/Printer3D/Repetier/index.js b/config/targets/Printer3D/Repetier/index.js new file mode 100644 index 00000000..9a06b810 --- /dev/null +++ b/config/targets/Printer3D/Repetier/index.js @@ -0,0 +1,867 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 +const roomTemperature = 20 +const temperatures = { + T: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + { + value: 20, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + B: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.2, + coolspeed: 0.8, + variation: 0.5, + }, + ], + C: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + P: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 0.5 }], //let say probe is 50% of extruder temperature + R: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 1 }], //the redondant is same as extruder 0 + M: [{ value: roomTemperature, lastTime: -1, variation: 1 }], //the motherboard is same as room temperature +5/10 degres +} + +const updateTemperature = (entry, time) => { + if (entry.lastTime == -1) { + entry.lastTime = time + } + + const v = Math.random() * 5 + //heater + if (typeof entry.target != "undefined") { + const target = entry.target == 0 ? roomTemperature : entry.target + if (entry.value + 5 < target) { + entry.value = + entry.value + (entry.heatspeed * (time - entry.lastTime)) / 1000 + } else if (entry.value - 5 > target) { + entry.value = + entry.value - (entry.coolspeed * (time - entry.lastTime)) / 1000 + } else if (target - 2 < entry.value && entry.value < target + 2) { + entry.value = target + entry.variation * (Math.random() - 0.5) + } else if (entry.value < target) { + entry.value = + entry.value + + ((entry.heatspeed / 3) * (time - entry.lastTime)) / 1000 + } else { + entry.value = + entry.value - + ((entry.coolspeed / 3) * (time - entry.lastTime)) / 1000 + } + } + //sensor + else if (typeof entry.sameas != "undefined") { + if ( + entry.sameas == "T" && + entry.variation == 0.5 && + entry.value < 2 * roomTemperature + ) { + //probe is same as room temperature if under 40 degres + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } else { + entry.value = + v > 2.5 + ? temperatures[entry.sameas][0].value * entry.variation + + v / 4 + : temperatures[entry.sameas][0].value * entry.variation - + v / 4 + } + } else { + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } + entry.lastTime = time +} + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + Object.keys(temperatures).map((tool) => { + temperatures[tool].map((entry) => { + updateTemperature(entry, new Date()) + }) + }) + const result = + "T:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " B:" + + Number(temperatures["B"][0].value).toFixed(2) + + " /" + + Number(temperatures["B"][0].target).toFixed(2) + + " T0:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " T1:" + + Number(temperatures["T"][1].value).toFixed(2) + + " /" + + Number(temperatures["T"][1].target).toFixed(2) + + " @:0 B@:0 @:0\nok\n" + console.log(result) + return result +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.0000\nok\n`) + res.send("") + return + } + if (url.indexOf("SIM:") != -1) { + const response = url.substring(url.indexOf("SIM:") + 4) + SendWS(response + "\n" + "ok\n") + res.send("") + return + } + + if (url.indexOf("M205") != -1) { + SendWS( + "EPR:0 1028 0 Language\n" + + "EPR:2 75 230400 Baudrate\n" + + "EPR:0 1125 1 Display Mode:\n" + + "EPR:0 1119 1 Light On:\n" + + "EPR:0 1127 1 Keep Light On:\n" + + "EPR:0 1126 0 Filament Sensor On:\n" + + "EPR:0 1176 0 Top Sensor On:\n" + + "EPR:0 1120 1 Sound On:\n" + + "EPR:0 1177 1 Wifi On:\n" + + "EPR:3 129 16.065 Filament printed [m]\n" + + "EPR:2 125 21576 Printer active [s]\n" + + "EPR:2 79 0 Max. inactive time [ms,0=off]\n" + + "EPR:2 83 360000 Stop stepper after inactivity [ms,0=off]\n" + + "EPR:2 1121 0 Powersave after [ms,0=off]:\n" + + "EPR:3 1160 180.000 Temp Ext PLA:\n" + + "EPR:3 1164 230.000 Temp Ext ABS:\n" + + "EPR:3 1168 60.000 Temp Bed PLA:\n" + + "EPR:3 1172 90.000 Temp Bed ABS:\n" + + "EPR:3 1179 2.000 Load Feed Rate:\n" + + "EPR:3 1183 4.000 Unload Feed Rate:\n" + + "EPR:3 1187 60.000 Unload/Load Distance:\n" + + "EPR:3 3 80.0000 X-axis steps per mm\n" + + "EPR:3 7 80.0000 Y-axis steps per mm\n" + + "EPR:3 11 2560.0000 Z-axis steps per mm\n" + + "EPR:3 15 200.000 X-axis max. feedrate [mm/s]\n" + + "EPR:3 19 200.000 Y-axis max. feedrate [mm/s]\n" + + "EPR:3 23 5.000 Z-axis max. feedrate [mm/s]\n" + + "EPR:3 27 40.000 X-axis homing feedrate [mm/s]\n" + + "EPR:3 31 40.000 Y-axis homing feedrate [mm/s]\n" + + "EPR:3 35 4.000 Z-axis homing feedrate [mm/s]\n" + + "EPR:3 39 20.000 Max. jerk [mm/s]\n" + + "EPR:3 47 0.342 Max. Z-jerk [mm/s]\n" + + "EPR:3 133 0.000 X min pos [mm]\n" + + "EPR:3 137 0.000 Y min pos [mm]\n" + + "EPR:3 141 0.000 Z min pos [mm]\n" + + "EPR:3 145 199.000 X max length [mm]\n" + + "EPR:3 149 204.000 Y max length [mm]\n" + + "EPR:3 153 200.000 Z max length [mm]\n" + + "EPR:3 51 1000.000 X-axis acceleration [mm/s^2]\n" + + "EPR:3 55 1000.000 Y-axis acceleration [mm/s^2]\n" + + "EPR:3 59 100.000 Z-axis acceleration [mm/s^2]\n" + + "EPR:3 63 1000.000 X-axis travel acceleration [mm/s^2]\n" + + "EPR:3 67 1000.000 Y-axis travel acceleration [mm/s^2]\n" + + "EPR:3 71 150.000 Z-axis travel acceleration [mm/s^2]\n" + + "EPR:3 1024 0.000 Coating thickness [mm]\n" + + "EPR:3 1128 100.000 Manual-probe X1 [mm]\n" + + "EPR:3 1132 180.000 Manual-probe Y1 [mm]\n" + + "EPR:3 1136 100.000 Manual-probe X2 [mm]\n" + + "EPR:3 1140 10.000 Manual-probe Y2 [mm]\n" + + "EPR:3 1144 50.000 Manual-probe X3 [mm]\n" + + "EPR:3 1148 95.000 Manual-probe Y3 [mm]\n" + + "EPR:3 1152 150.000 Manual-probe X4 [mm]\n" + + "EPR:3 1156 95.000 Manual-probe Y4 [mm]\n" + + "EPR:3 808 0.280 Z-probe height [mm]\n" + + "EPR:3 929 5.000 Max. z-probe - bed dist. [mm]\n" + + "EPR:3 812 1.000 Z-probe speed [mm/s]\n" + + "EPR:3 840 30.000 Z-probe x-y-speed [mm/s]\n" + + "EPR:3 800 0.000 Z-probe offset x [mm]\n" + + "EPR:3 804 0.000 Z-probe offset y [mm]\n" + + "EPR:3 816 36.000 Z-probe X1 [mm]\n" + + "EPR:3 820 -7.000 Z-probe Y1 [mm]\n" + + "EPR:3 824 36.000 Z-probe X2 [mm]\n" + + "EPR:3 828 203.000 Z-probe Y2 [mm]\n" + + "EPR:3 832 171.000 Z-probe X3 [mm]\n" + + "EPR:3 836 203.000 Z-probe Y3 [mm]\n" + + "EPR:3 1036 0.000 Z-probe bending correction A [mm]\n" + + "EPR:3 1040 0.000 Z-probe bending correction B [mm]\n" + + "EPR:3 1044 0.000 Z-probe bending correction C [mm]\n" + + "EPR:0 880 0 Autolevel active (1/0)\n" + + "EPR:0 106 2 Bed Heat Manager [0-3]\n" + + "EPR:0 107 255 Bed PID drive max\n" + + "EPR:0 124 80 Bed PID drive min\n" + + "EPR:3 108 196.000 Bed PID P-gain\n" + + "EPR:3 112 33.000 Bed PID I-gain\n" + + "EPR:3 116 290.000 Bed PID D-gain\n" + + "EPR:0 120 255 Bed PID max value [0-255]\n" + + "EPR:0 1020 0 Enable retraction conversion [0/1]\n" + + "EPR:3 992 3.000 Retraction length [mm]\n" + + "EPR:3 996 13.000 Retraction length extruder switch [mm]\n" + + "EPR:3 1000 40.000 Retraction speed [mm/s]\n" + + "EPR:3 1004 0.000 Retraction z-lift [mm]\n" + + "EPR:3 1008 0.000 Extra extrusion on undo retract [mm]\n" + + "EPR:3 1012 0.000 Extra extrusion on undo switch retract [mm]\n" + + "EPR:3 1016 20.000 Retraction undo speed\n" + + "EPR:3 200 99.000 Extr.1 steps per mm\n" + + "EPR:3 204 50.000 Extr.1 max. feedrate [mm/s]\n" + + "EPR:3 208 20.000 Extr.1 start feedrate [mm/s]\n" + + "EPR:3 212 5000.000 Extr.1 acceleration [mm/s^2]\n" + + "EPR:0 216 1 Extr.1 heat manager [0-3]\n" + + "EPR:0 217 230 Extr.1 PID drive max\n" + + "EPR:0 245 40 Extr.1 PID drive min\n" + + "EPR:3 218 3.0000 Extr.1 PID P-gain/dead-time\n" + + "EPR:3 222 2.0000 Extr.1 PID I-gain\n" + + "EPR:3 226 40.0000 Extr.1 PID D-gain\n" + + "EPR:0 230 255 Extr.1 PID max value [0-255]\n" + + "EPR:2 231 0 Extr.1 X-offset [steps]\n" + + "EPR:2 235 0 Extr.1 Y-offset [steps]\n" + + "EPR:2 290 0 Extr.1 Z-offset [steps]\n" + + "EPR:1 239 1 Extr.1 temp. stabilize time [s]\n" + + "EPR:1 250 150 Extr.1 temp. for retraction when heating [C]\n" + + "EPR:1 252 0 Extr.1 distance to retract when heating [mm]\n" + + "EPR:0 254 255 Extr.1 extruder cooler speed [0-255]\n" + + "EPR:3 246 0.000 Extr.1 advance L [0=off]\n" + + "EPR:3 300 99.000 Extr.2 steps per mm\n" + + "EPR:3 304 50.000 Extr.2 max. feedrate [mm/s]\n" + + "EPR:3 308 20.000 Extr.2 start feedrate [mm/s]\n" + + "EPR:3 312 5000.000 Extr.2 acceleration [mm/s^2]\n" + + "EPR:0 316 3 Extr.2 heat manager [0-3]\n" + + "EPR:0 317 230 Extr.2 PID drive max\n" + + "EPR:0 345 40 Extr.2 PID drive min\n" + + "EPR:3 318 3.0000 Extr.2 PID P-gain/dead-time\n" + + "EPR:3 322 2.0000 Extr.2 PID I-gain\n" + + "EPR:3 326 40.0000 Extr.2 PID D-gain\n" + + "EPR:0 330 255 Extr.2 PID max value [0-255]\n" + + "EPR:2 331 -2852 Extr.2 X-offset [steps]\n" + + "EPR:2 335 12 Extr.2 Y-offset [steps]\n" + + "EPR:2 390 0 Extr.2 Z-offset [steps]\n" + + "EPR:1 339 1 Extr.2 temp. stabilize time [s]\n" + + "EPR:1 350 150 Extr.2 temp. for retraction when heating [C]\n" + + "EPR:1 352 0 Extr.2 distance to retract when heating [mm]\n" + + "EPR:0 354 255 Extr.2 extruder cooler speed [0-255]\n" + + "EPR:3 346 0.000 Extr.2 advance L [0=off]\n" + + "wait\n" + ) + res.send("") + return + } + + if (url.indexOf("M206") != -1) { + SendWS("wait\n") + res.send("") + return + } + if (url.indexOf("M104") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const reg_ex_index = /T([0-9])/ + const result_target = reg_ex_temp.exec(url) + const result_index = reg_ex_index.exec(url) + console.log(result_target[1], result_index[1]) + temperatures["T"][result_index[1]].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M140") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["B"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "ok 0\n" + + "Begin file list\n" + + "TEST.ZIP 66622272\n" + + "ad_check_0001 2400\n" + + "Machine_Life.dat 0\n" + + "SOUND8.G 992\n" + + "eeprom.bin 4096\n" + + "FW_upgrade.dat 0\n" + + "MYFOLDER/\n" + + "MYFOLDER/sound.g 1040\n" + + "MYFOLDER/Lucdir/\n" + + "M33.G 90\n" + + "M3.G 90\n" + + "SUPPOR~1.GCO 1226740\n" + + "TEST1~1.GCO 113\n" + + "T1.G 21\n" + + "smal.gco 81\n" + + "testgcode.gco 14\n" + + "fr.json 0\n" + + "End file list\n" + + "wait\n" + ) + res.send("") + return + } + + if (url.indexOf("M32") != -1) { + const name = url.split(" ") + SendWS( + //"Creation failed\n" + "Directory created\n" + ) + + res.send("") + return + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Repetier_0.92.10 FIRMWARE_URL:https://github.com/luc-github/Repetier-Firmware-0.92/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:DaVinci EXTRUDER_COUNT:2 REPETIER_PROTOCOL:3\n" + + "Cap:PROGRESS:1\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:EEPROM:0\n" + + "Cap:TOGGLE_LIGHTS:1\n" + + "Printed filament:19.23m Printing time:0 days 6 hours 43 min\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "repetier", + FWTargetID: "50", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: enableAuthentication ? "Enabled" : "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "repetier" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "50", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: repetier
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/Printer3D/Smoothieware/index.js b/config/targets/Printer3D/Smoothieware/index.js new file mode 100644 index 00000000..29c628ef --- /dev/null +++ b/config/targets/Printer3D/Smoothieware/index.js @@ -0,0 +1,1125 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 +const roomTemperature = 20 +const temperatures = { + T: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + { + value: 20, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + B: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.2, + coolspeed: 0.8, + variation: 0.5, + }, + ], + C: [ + { + value: roomTemperature, + target: 0, + lastTime: -1, + heatspeed: 0.6, + coolspeed: 0.8, + variation: 0.5, + }, + ], + P: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 0.5 }], //let say probe is 50% of extruder temperature + R: [{ value: roomTemperature, sameas: "T", lastTime: -1, variation: 1 }], //the redondant is same as extruder 0 + M: [{ value: roomTemperature, lastTime: -1, variation: 1 }], //the motherboard is same as room temperature +5/10 degres +} + +const updateTemperature = (entry, time) => { + if (entry.lastTime == -1) { + entry.lastTime = time + } + + const v = Math.random() * 5 + //heater + if (typeof entry.target != "undefined") { + const target = entry.target == 0 ? roomTemperature : entry.target + if (entry.value + 5 < target) { + entry.value = + entry.value + (entry.heatspeed * (time - entry.lastTime)) / 1000 + } else if (entry.value - 5 > target) { + entry.value = + entry.value - (entry.coolspeed * (time - entry.lastTime)) / 1000 + } else if (target - 2 < entry.value && entry.value < target + 2) { + entry.value = target + entry.variation * (Math.random() - 0.5) + } else if (entry.value < target) { + entry.value = + entry.value + + ((entry.heatspeed / 3) * (time - entry.lastTime)) / 1000 + } else { + entry.value = + entry.value - + ((entry.coolspeed / 3) * (time - entry.lastTime)) / 1000 + } + } + //sensor + else if (typeof entry.sameas != "undefined") { + if ( + entry.sameas == "T" && + entry.variation == 0.5 && + entry.value < 2 * roomTemperature + ) { + //probe is same as room temperature if under 40 degres + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } else { + entry.value = + v > 2.5 + ? temperatures[entry.sameas][0].value * entry.variation + + v / 4 + : temperatures[entry.sameas][0].value * entry.variation - + v / 4 + } + } else { + entry.value = + v > 2.5 ? roomTemperature + v / 2 : roomTemperature - v / 2 + } + entry.lastTime = time +} + +function getLastconnection() { + return lastconnection +} + +function hasEnabledAuthentication() { + return enableAuthentication +} + +function Temperatures() { + Object.keys(temperatures).map((tool) => { + temperatures[tool].map((entry) => { + updateTemperature(entry, new Date()) + }) + }) + const result = + "ok T:" + + Number(temperatures["T"][0].value).toFixed(2) + + " /" + + Number(temperatures["T"][0].target).toFixed(2) + + " @0 B:" + + Number(temperatures["B"][0].value).toFixed(2) + + " /" + + Number(temperatures["B"][0].target).toFixed(2) + + "@0 T1:" + + Number(temperatures["T"][1].value).toFixed(2) + + " /" + + Number(temperatures["T"][1].target).toFixed(2) + + " @0\n" + console.log(result) + return result +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return + } + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2) + let Y = Number(Math.random() * 200.12).toFixed(2) + let Z = Number(Math.random() * 200.12).toFixed(2) + SendWS(`ok C: X:${X} Y:${Y} Z:${Z} E:0.0000\n`) + res.send("") + return + } + if (url.indexOf("SIM:") != -1) { + const response = url.substring(url.indexOf("SIM:") + 4) + SendWS(response + "\n" + "ok\n") + res.send("") + return + } + if (url.indexOf("ls -s /sd") != -1) { + if (url.indexOf("echo BeginFiles") != -1) SendWS("echo: BeginFiles\n") + SendWS( + "found.000/\n" + + "t2.g 112023\n" + + "calibration/\n" + + "firmware.cur 391256\n" + + "config-override 1010\n" + + "pattern-holder.gcode 9754891\n" + + "system volume information/\n" + + "pattern-holder2.gcode 9754891\n" + + "config.txt 23136\n" + + "webif/\n" + + "acerlogo.jpeg 6257\n" + + "a.dat 6257\n" + + "firmware.cur.printer 389776\n" + ) + if (url.indexOf("echo EndFiles") != -1) SendWS("echo: EndFiles\n") + res.send("") + return + } + + if (url.indexOf("ls -s /ext") != -1) { + if (url.indexOf("echo BeginFiles") != -1) SendWS("echo: BeginFiles\n") + SendWS( + "system volume information/\n" + + "pattern-holder2.gcode 9754891\n" + + "config.txt 23136\n" + + "webif/\n" + + "acerlogo.jpeg 6257\n" + + "a.dat 6257\n" + + "firmware.cur.printer 389776\n" + ) + if (url.indexOf("echo EndFiles") != -1) SendWS("echo: EndFiles\n") + res.send("") + return + } + + if (url.indexOf("M20") != -1) { + SendWS( + "Begin file list\n" + + "found.000/\n" + + "t2.g\n" + + "calibration/\n" + + "firmware.cur\n" + + "config-override\n" + + "pattern-holder.gcode\n" + + "system volume information/\n" + + "pattern-holder2.gcode\n" + + "config.txt\n" + + "webif/\n" + + "acerlogo.jpeg\n" + + "a.dat\n" + + "firmware.cur.printer\n" + + "End file list\n" + + "ok\n" + ) + res.send("") + return + } + + if (url.startsWith("echo ")) { + console.log("yes") + const response = url.replace("echo ", "echo: ") + console.log("Resp:", response) + SendWS(response + "\n") + res.send("") + return + } + if (url.indexOf("M30") != -1) { + const name = url.split(" ") + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ) + + res.send("") + return + } + + if (url.indexOf("M140") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const result_target = reg_ex_temp.exec(url) + temperatures["B"][0].target = parseFloat(result_target[1]) + res.send("") + return + } + + if (url.indexOf("M104") != -1) { + const reg_ex_temp = /S([0-9]*\.?[0-9]*)/ + const reg_ex_index = /T([0-9])/ + const result_target = reg_ex_temp.exec(url) + const result_index = reg_ex_index.exec(url) + console.log(result_target[1], result_index[1]) + temperatures["T"][result_index[1]].target = parseFloat(result_target[1]) + res.send("") + return + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Smoothieware, FIRMWARE_URL:http%3A//smoothieware.org, X-SOURCE_CODE_URL:https://github.com/Smoothieware/Smoothieware, FIRMWARE_VERSION:edge-0565b13, PROTOCOL_VERSION:1.0, X-FIRMWARE_BUILD_DATE:Jun 19 2021 16:12:18, X-SYSTEM_CLOCK:100MHz, X-AXES:5, X-GRBL_MODE:0, X-ARCS:1, X-CNC:0, X-MSD:1, X-WARNING:deprecated_MCU\nok\n" + ) + res.send("") + return + } + if (url.indexOf("cat /sd/config") != -1) { + SendWS( + "# Robot module configurations : general handling of movement G-codes and slicing into moves\n" + + "default_feed_rate 1000 # Default rate ( mm/minute ) for G1/G2/G3 moves\n" + + "default_seek_rate 1000 # Default rate ( mm/minute ) for G0 moves\n" + + "mm_per_arc_segment 0.5 # Arcs are cut into segments ( lines ), this is the length for these segments. Smaller values mean more resolution, higher values mean faster computation\n" + + "#mm_per_line_segment 5 # Lines can be cut into segments ( not usefull with cartesian coordinates robots ).\n" + + "\n" + + "# Arm solution configuration : Cartesian robot. Translates mm positions into stepper positions\n" + + "alpha_steps_per_mm 80 # Steps per mm for alpha stepper\n" + + "beta_steps_per_mm 80 # Steps per mm for beta stepper\n" + + "gamma_steps_per_mm 1637.7953 # Steps per mm for gamma stepper\n" + + "\n" + + "# Planner module configuration : Look-ahead and acceleration configuration\n" + + "planner_queue_size 32 # DO NOT CHANGE THIS UNLESS YOU KNOW EXACTLY WHAT YOUR ARE DOING\n" + + "acceleration 1000 # Acceleration in mm/second/second.\n" + + "#z_acceleration 60 # Acceleration for Z only moves in mm/s^2, 0 disables it, disabled by default. DO NOT SET ON A DELTA\n" + + "acceleration_ticks_per_second 1000 # Number of times per second the speed is updated\n" + + 'junction_deviation 0.05 # Similar to the old "max_jerk", in millimeters, see : https://github.com/grbl/grbl/blob/master/planner.c#L409\n' + + " # and https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8 . Lower values mean being more careful, higher values means being faster and have more jerk\n" + + "\n" + + "# Stepper module configuration\n" + + "microseconds_per_step_pulse 1 # Duration of step pulses to stepper drivers, in microseconds\n" + + "base_stepping_frequency 100000 # Base frequency for stepping\n" + + "\n" + + '# Stepper module pins ( ports, and pin numbers, appending "!" to the number will invert a pin )\n' + + "alpha_step_pin 2.1 # Pin for alpha stepper step signal\n" + + "alpha_dir_pin 0.11 # Pin for alpha stepper direction\n" + + "alpha_en_pin 0.10 # Pin for alpha enable pin 0.10\n" + + "alpha_current 1.0 # X stepper motor current\n" + + "x_axis_max_speed 1000 # mm/min\n" + + "alpha_max_rate 1000.0 # mm/min actuator max speed\n" + + "\n" + + "beta_step_pin 2.2 # Pin for beta stepper step signal\n" + + "beta_dir_pin 0.20 # Pin for beta stepper direction\n" + + "beta_en_pin 0.19 # Pin for beta enable\n" + + "beta_current 1.0 # Y stepper motor current\n" + + "y_axis_max_speed 1000 # mm/min\n" + + "beta_max_rate 1000.0 # mm/min actuator max speed\n" + + "\n" + + "gamma_step_pin 2.3 # Pin for gamma stepper step signal\n" + + "gamma_dir_pin 0.22 # Pin for gamma stepper direction\n" + + "gamma_en_pin 0.21 # Pin for gamma enable\n" + + "gamma_current 1.0 # Z stepper motor current\n" + + "z_axis_max_speed 60 # mm/min\n" + + "gamma_max_rate 60.0 # mm/min actuator max speed\n" + + "\n" + + "# Serial communications configuration ( baud rate default to 9600 if undefined )\n" + + "uart0.baud_rate 115200 # Baud rate for the default hardware serial port\n" + + "second_usb_serial_enable false # This enables a second usb serial port (to have both pronterface and a terminal connected)\n" + + "msd_disable true # disable the MSD (USB SDCARD) when set to true\n" + + "\n" + + "\n" + + "# Extruder module configuration\n" + + "extruder.hotend.enable true # Whether to activate the extruder module at all. All configuration is ignored if false\n" + + "#extruder.hotend.steps_per_mm 140 # Steps per mm for extruder stepper\n" + + "#extruder.hotend.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves\n" + + "#extruder.hotend.acceleration 500 # Acceleration for the stepper motor mm/sec2\n" + + "#extruder.hotend.max_speed 50 # mm/s\n" + + "\n" + + "#extruder.hotend.step_pin 2.0 # Pin for extruder step signal\n" + + "#extruder.hotend.dir_pin 0.5 # Pin for extruder dir signal\n" + + "#extruder.hotend.en_pin 0.4 # Pin for extruder enable signal\n" + + "\n" + + "# extruder offset\n" + + "#extruder.hotend.x_offset 0 # x offset from origin in mm\n" + + "#extruder.hotend.y_offset 0 # y offset from origin in mm\n" + + "#extruder.hotend.z_offset 0 # z offset from origin in mm\n" + + "\n" + + "# firmware retract settings when using G10/G11, these are the defaults if not defined, must be defined for each extruder if not using the defaults\n" + + "#extruder.hotend.retract_length 3 # retract length in mm\n" + + "#extruder.hotend.retract_feedrate 45 # retract feedrate in mm/sec\n" + + "#extruder.hotend.retract_recover_length 0 # additional length for recover\n" + + "#extruder.hotend.retract_recover_feedrate 8 # recover feedrate in mm/sec (should be less than retract feedrate)\n" + + "#extruder.hotend.retract_zlift_length 0 # zlift on retract in mm, 0 disables\n" + + "#extruder.hotend.retract_zlift_feedrate 6000 # zlift feedrate in mm/min (Note mm/min NOT mm/sec)\n" + + "delta_current 1.0 # Extruder stepper motor current\n" + + "\n" + + "# Second extruder module configuration\n" + + "extruder.hotend2.enable true # Whether to activate the extruder module at all. All configuration is ignored if false\n" + + "extruder.hotend2.steps_per_mm 140 # Steps per mm for extruder stepper\n" + + "extruder.hotend2.default_feed_rate 600 # Default rate ( mm/minute ) for moves where only the extruder moves\n" + + "extruder.hotend2.acceleration 500 # Acceleration for the stepper motor, as of 0.6, arbitrary ratio\n" + + "extruder.hotend2.max_speed 50 # mm/s\n" + + "\n" + + "extruder.hotend2.step_pin 2.8 # Pin for extruder step signal\n" + + "extruder.hotend2.dir_pin 2.13 # Pin for extruder dir signal\n" + + "extruder.hotend2.en_pin 4.29 # Pin for extruder enable signal\n" + + "\n" + + "extruder.hotend2.x_offset 0 # x offset from origin in mm\n" + + "extruder.hotend2.y_offset 25.0 # y offset from origin in mm\n" + + "extruder.hotend2.z_offset 0 # z offset from origin in mm\n" + + "epsilon_current 1.5 # Second extruder stepper motor current\n" + + "\n" + + "\n" + + "\n" + + "# Laser module configuration\n" + + "laser_module_enable false # Whether to activate the laser module at all. All configuration is ignored if false.\n" + + "laser_module_pin 2.7 # this pin will be PWMed to control the laser\n" + + "laser_module_max_power 0.8 # this is the maximum duty cycle that will be applied to the laser\n" + + "laser_module_tickle_power 0.0 # this duty cycle will be used for travel moves to keep the laser active without actually burning\n" + + "\n" + + "# Hotend temperature control configuration\n" + + 'temperature_control.hotend.enable true # Whether to activate this ( "hotend" ) module at all. All configuration is ignored if false.\n' + + "#temperature_control.hotend.thermistor_pin 0.23 # Pin for the thermistor to read\n" + + "#temperature_control.hotend.heater_pin 2.5 # Pin that controls the heater\n" + + "#temperature_control.hotend.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5\n" + + "#temperature_control.hotend.beta 4066 # or set the beta value\n" + + "\n" + + "#temperature_control.hotend.set_m_code 104 #\n" + + "#temperature_control.hotend.set_and_wait_m_code 109 #\n" + + "#temperature_control.hotend.designator T #\n" + + "\n" + + "#temperature_control.hotend.p_factor 13.7 #\n" + + "#temperature_control.hotend.i_factor 0.097 #\n" + + "#temperature_control.hotend.d_factor 24 #\n" + + "\n" + + "#temperature_control.hotend.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v.\n" + + "\n" + + "# Hotend2 temperature control configuration\n" + + 'temperature_control.hotend2.enable false # Whether to activate this ( "hotend" ) module at all. All configuration is ignored if false.\n' + + "\n" + + "#temperature_control.hotend2.thermistor_pin 0.25 # Pin for the thermistor to read\n" + + "#temperature_control.hotend2.heater_pin 2.4 # Pin that controls the heater\n" + + "#temperature_control.hotend2.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5\n" + + "##temperature_control.hotend2.beta 4066 # or set the beta value\n" + + "#temperature_control.hotend2.set_m_code 104 #\n" + + "#temperature_control.hotend2.set_and_wait_m_code 109 #\n" + + "#temperature_control.hotend2.designator T1 #\n" + + "\n" + + "#temperature_control.hotend2.p_factor 13.7 # permanently set the PID values after an auto pid\n" + + "#temperature_control.hotend2.i_factor 0.097 #\n" + + "#temperature_control.hotend2.d_factor 24 #\n" + + "\n" + + "#temperature_control.hotend2.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v.\n" + + "\n" + + "temperature_control.bed.enable false #\n" + + "#temperature_control.bed.thermistor_pin 0.24 #\n" + + "#temperature_control.bed.heater_pin 2.7 #\n" + + "#temperature_control.bed.thermistor EPCOS100K # see http://smoothieware.org/temperaturecontrol#toc5\n" + + "#temperature_control.bed.beta 4066 # or set the beta value\n" + + "\n" + + "#temperature_control.bed.set_m_code 140 #\n" + + "#temperature_control.bed.set_and_wait_m_code 190 #\n" + + "#temperature_control.bed.designator B #\n" + + "\n" + + "#temperature_control.bed.max_pwm 64 # max pwm, 64 is a good value if driving a 12v resistor with 24v.\n" + + "\n" + + "# Switch module for led control\n" + + "switch.led.enable true #\n" + + "switch.led.input_on_command M800 #\n" + + "switch.led.input_off_command M801 #\n" + + "switch.led.output_pin 2.5 #\n" + + "switch.led.output_type digital #\n" + + "switch.led.startup_value 1 #\n" + + "switch.led.startup_state true #\n" + + "\n" + + "switch.misc.enable false #\n" + + "switch.misc.input_on_command M42 #\n" + + "switch.misc.input_off_command M43 #\n" + + "switch.misc.output_pin 2.4 #\n" + + "\n" + + "# automatically toggle a switch at a specified temperature. Different ones of these may be defined to monitor different temperatures and switch different swithxes\n" + + "# useful to turn on a fan or water pump to cool the hotend\n" + + "#temperatureswitch.hotend.enable true #\n" + + "#temperatureswitch.hotend.designator T # first character of the temperature control designator to use as the temperature sensor to monitor\n" + + "#temperatureswitch.hotend.switch misc # select which switch to use, matches the name of the defined switch\n" + + "#temperatureswitch.hotend.threshold_temp 60.0 # temperature to turn on (if rising) or off the switch\n" + + "#temperatureswitch.hotend.heatup_poll 15 # poll heatup at 15 sec intervals\n" + + "#temperatureswitch.hotend.cooldown_poll 60 # poll cooldown at 60 sec intervals\n" + + "\n" + + "# filament out detector\n" + + "#filament_detector.enable true #\n" + + "#filament_detector.encoder_pin 0.26 # must be interrupt enabled pin (0.26, 0.27, 0.28)\n" + + "#filament_detector.seconds_per_check 2 # may need to be longer\n" + + "#filament_detector.pulses_per_mm 1 .0 # will need to be tuned\n" + + "#filament_detector.bulge_pin 0.27 # optional bulge detector switch and/or manual suspend\n" + + "\n" + + "# Switch module for spindle control\n" + + "#switch.spindle.enable false #\n" + + "\n" + + "# Endstops\n" + + "endstops_enable true # the endstop module is enabled by default and can be disabled here\n" + + "#corexy_homing false # set to true if homing on a hbit or corexy\n" + + "alpha_min_endstop 1.24^ # add a ! to invert if endstop is NO connected to ground\n" + + "#alpha_max_endstop 1.24^ #\n" + + "alpha_homing_direction home_to_min # or set to home_to_max and set alpha_max\n" + + "alpha_min 0 # this gets loaded after homing when home_to_min is set\n" + + "alpha_max 380 # this gets loaded after homing when home_to_max is set\n" + + "beta_min_endstop 1.26^ #\n" + + "#beta_max_endstop 1.26^ #\n" + + "beta_homing_direction home_to_min #\n" + + "beta_min 0 #\n" + + "beta_max 440 #\n" + + "gamma_min_endstop 1.29^ #\n" + + "#gamma_max_endstop 1.29^ #\n" + + "gamma_homing_direction home_to_min #\n" + + "gamma_min 0 #\n" + + "gamma_max 180 #\n" + + "\n" + + "# optional enable limit switches, actions will stop if any enabled limit switch is triggered\n" + + "#alpha_limit_enable false # set to true to enable X min and max limit switches\n" + + "#beta_limit_enable false # set to true to enable Y min and max limit switches\n" + + "#gamma_limit_enable false # set to true to enable Z min and max limit switches\n" + + "\n" + + "#probe endstop\n" + + "#probe_pin 1.29 # optional pin for probe\n" + + "\n" + + "alpha_fast_homing_rate_mm_s 50 # feedrates in mm/second\n" + + 'beta_fast_homing_rate_mm_s 50 # "\n' + + 'gamma_fast_homing_rate_mm_s 4 # "\n' + + 'alpha_slow_homing_rate_mm_s 25 # "\n' + + 'beta_slow_homing_rate_mm_s 25 # "\n' + + 'gamma_slow_homing_rate_mm_s 2 # "\n' + + "\n" + + "alpha_homing_retract_mm 5 # distance in mm\n" + + 'beta_homing_retract_mm 5 # "\n' + + 'gamma_homing_retract_mm 5 # "\n' + + "\n" + + "#endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100\n" + + "\n" + + "# optional Z probe\n" + + "zprobe.enable false # set to true to enable a zprobe\n" + + "#zprobe.probe_pin 1.29!^ # pin probe is attached to if NC remove the !\n" + + "#zprobe.slow_feedrate 5 # mm/sec probe feed rate\n" + + "#zprobe.debounce_count 100 # set if noisy\n" + + "#zprobe.fast_feedrate 100 # move feedrate mm/sec\n" + + "#zprobe.probe_height 5 # how much above bed to start probe\n" + + "\n" + + "# associated with zprobe the leveling strategy to use\n" + + "#leveling-strategy.three-point-leveling.enable true # a leveling strategy that probes three points to define a plane and keeps the Z parallel to that plane\n" + + "#leveling-strategy.three-point-leveling.point1 100.0,0.0 # the first probe point (x,y) optional may be defined with M557\n" + + "#leveling-strategy.three-point-leveling.point2 200.0,200.0 # the second probe point (x,y)\n" + + "#leveling-strategy.three-point-leveling.point3 0.0,200.0 # the third probe point (x,y)\n" + + "#leveling-strategy.three-point-leveling.home_first true # home the XY axis before probing\n" + + "#leveling-strategy.three-point-leveling.tolerance 0.03 # the probe tolerance in mm, anything less that this will be ignored, default is 0.03mm\n" + + "#leveling-strategy.three-point-leveling.probe_offsets 0,0,0 # the probe offsets from nozzle, must be x,y,z, default is no offset\n" + + "#leveling-strategy.three-point-leveling.save_plane false # set to true to allow the bed plane to be saved with M500 default is false\n" + + "\n" + + "\n" + + "# Pause button\n" + + "pause_button_enable true #\n" + + "\n" + + "# Panel See http://smoothieware.org/panel\n" + + "panel.enable true # set to true to enable the panel code\n" + + "\n" + + "# Example viki2 config for an azteeg miniV2 with IDC cable\n" + + "panel.lcd viki2 # set type of panel\n" + + "panel.spi_channel 0 # set spi channel to use P0_18,P0_15 MOSI,SCLK\n" + + "panel.spi_cs_pin 0.16 # set spi chip select\n" + + "panel.encoder_a_pin 3.25!^ # encoder pin\n" + + "panel.encoder_b_pin 3.26!^ # encoder pin\n" + + "panel.click_button_pin 2.11!^ # click button\n" + + "panel.a0_pin 2.6 # st7565 needs an a0\n" + + "panel.contrast 8 # override contrast setting (default is 9)\n" + + "panel.encoder_resolution 4 # override number of clicks to move 1 item (default is 4)\n" + + "#panel.button_pause_pin 1.22^ # kill/pause set one of these for the auxilliary button on viki2\n" + + "#panel.back_button_pin 1.22!^ # back button recommended to use this on EXP1\n" + + "panel.buzz_pin 1.30 # pin for buzzer on EXP2\n" + + "panel.red_led_pin 0.26 # pin for red led on viki2 on EXP1\n" + + "panel.blue_led_pin 1.21 # pin for blue led on viki2 on EXP1\n" + + "panel.external_sd true # set to true if there is an extrernal sdcard on the panel\n" + + "panel.external_sd.spi_channel 0 # set spi channel the sdcard is on\n" + + "panel.external_sd.spi_cs_pin 1.23 # set spi chip select for the sdcard\n" + + "panel.external_sd.sdcd_pin 1.31!^ # sd detect signal (set to nc if no sdcard detect)\n" + + "panel.menu_offset 1 # some panels will need 1 here\n" + + "\n" + + "\n" + + "# Example miniviki2 config\n" + + "#panel.lcd mini_viki2 # set type of panel\n" + + "#panel.spi_channel 0 # set spi channel to use P0_18,P0_15 MOSI,SCLK\n" + + "#panel.spi_cs_pin 0.16 # set spi chip select\n" + + "#panel.encoder_a_pin 3.25!^ # encoder pin\n" + + "#panel.encoder_b_pin 3.26!^ # encoder pin\n" + + "#panel.click_button_pin 2.11!^ # click button\n" + + "#panel.a0_pin 2.6 # st7565 needs an a0\n" + + "##panel.contrast 18 # override contrast setting (default is 18)\n" + + "##panel.encoder_resolution 2 # override number of clicks to move 1 item (default is 2)\n" + + "#panel.menu_offset 1 # here controls how sensitive the menu is. some panels will need 1\n" + + "\n" + + "panel.alpha_jog_feedrate 1000 # x jogging feedrate in mm/min\n" + + "panel.beta_jog_feedrate 1000 # y jogging feedrate in mm/min\n" + + "panel.gamma_jog_feedrate 4 # z jogging feedrate in mm/min\n" + + "\n" + + "#panel.hotend_temperature 185 # temp to set hotend when preheat is selected\n" + + "#panel.T1_temperature 185 # temp to set hotend when preheat is selected\n" + + "#panel.bed_temperature 60 # temp to set bed when preheat is selected\n" + + "\n" + + "# Example of a custom menu entry, which will show up in the Custom entry.\n" + + "# NOTE _ gets converted to space in the menu and commands, | is used to separate multiple commands\n" + + "custom_menu.power_on.enable true #\n" + + "custom_menu.power_on.name Light_on #\n" + + "custom_menu.power_on.command M800 #\n" + + "\n" + + "custom_menu.power_off.enable true #\n" + + "custom_menu.power_off.name Light_off #\n" + + "custom_menu.power_off.command M801 #\n" + + "\n" + + "# RE-ARM specific settings do not change\n" + + "currentcontrol_module_enable false #\n" + + "digipot_max_current 2.4 # max current\n" + + "digipot_factor 106.0 # factor for converting current to digipot value\n" + + "leds_disable true # disable using leds after config loaded\n" + + "\n" + + "# network settings\n" + + "network.enable false # enable the ethernet network services\n" + + "#network.webserver.enable true # enable the webserver\n" + + "#network.telnet.enable true # enable the telnet server\n" + + "#network.plan9.enable true # enable the plan9 network filesystem\n" + + "#network.ip_address auto # the IP address\n" + + "#network.ip_mask 255.255.255.0 # the ip mask\n" + + "#network.ip_gateway 192.168.3.1 # the gateway address\n" + + "\n" + + "return_error_on_unhandled_gcode false #\n" + + "ok\n" + + "echo: configDone\n" + ) + + res.send("") + return + } + if (url.startsWith("version")) { + SendWS( + "Build version: edge-3332442, Build date: Apr 22 2016 15:52:55, MCU: LPC1768, System Clock: 100MHz\nok\n" + ) + res.send("") + return + } + if (url.indexOf("M503") != -1) { + SendWS( + "; config override present: /sd/config-override\n" + + ";Steps per unit:\n" + + "M92 X80.00000 Y80.00000 Z1637.79529 \n" + + ";Acceleration mm/sec^2:\n" + + "M204 S1000.00000 \n" + + ";X- Junction Deviation, Z- Z junction deviation, S - Minimum Planner speed mm/sec:\n" + + "M205 X0.05000 Z-1.00000 S0.00000\n" + + ";Max cartesian feedrates in mm/sec:\n" + + "M203 X16.66667 Y16.66667 Z1.00000 S-1.00000\n" + + ";Max actuator feedrates in mm/sec:\n" + + "M203.1 X16.66667 Y16.66667 Z1.00000 \n" + + ";E Steps per mm:\n" + + "M92 E1.0000 P57988\n" + + ";E Filament diameter:\n" + + "M200 D0.0000 P57988\n" + + ";E retract length, feedrate:\n" + + "M207 S3.0000 F2700.0000 Z0.0000 Q6000.0000 P57988\n" + + ";E retract recover length, feedrate:\n" + + "M208 S0.0000 F480.0000 P57988\n" + + ";E acceleration mm/sec²:\n" + + "M204 E1000.0000 P57988\n" + + ";E max feed rate mm/sec:\n" + + "M203 E1000.0000 P57988\n" + + ";E Steps per mm:\n" + + "M92 E140.0000 P39350\n" + + ";E Filament diameter:\n" + + "M200 D0.0000 P39350\n" + + ";E retract length, feedrate:\n" + + "M207 S3.0000 F2700.0000 Z0.0000 Q6000.0000 P39350\n" + + ";E retract recover length, feedrate:\n" + + "M208 S0.0000 F480.0000 P39350\n" + + ";E acceleration mm/sec²:\n" + + "M204 E500.0000 P39350\n" + + ";E max feed rate mm/sec:\n" + + "M203 E50.0000 P39350\n" + + ";Home offset (mm):\n" + + "M206 X0.00 Y0.00 Z0.00 \n" + + "ok\n" + + "echo: overrideDone\n" + ) + res.send("") + return + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()) + res.send("") + return + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "smoothieware", + FWTargetID: "40", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: enableAuthentication ? "Enabled" : "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "smoothesp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "smoothieware" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) + return + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [ + { none: "0" }, + { pushover: "1" }, + { email: "2" }, + { line: "3" }, + ], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }) + return + } + SendWS("ok\n") + res.send("") +} + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") +} + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: smoothieware
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) +} + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +} diff --git a/config/targets/SandTable/FluidNC/index.js b/config/targets/SandTable/FluidNC/index.js new file mode 100644 index 00000000..7d7df05b --- /dev/null +++ b/config/targets/SandTable/FluidNC/index.js @@ -0,0 +1,671 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk"); +const wscolor = chalk.cyan; +const expresscolor = chalk.green; +const commandcolor = chalk.white; +const enableAuthentication = false; +let lastconnection = Date.now(); +let logindone = false; +const sessiontTime = 60000; +let countStatus = 0; + +function getLastconnection() { + return lastconnection; +} + +function hasEnabledAuthentication() { + return enableAuthentication; +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl; + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)); + else console.log(commandcolor(`[server]/command : ${url}`)); + if (url.indexOf("PING") != -1) { + lastconnection = Date.now(); + res.status(200); + res.send("ok\n"); + console.log(commandcolor(`[server]/command :PING`)); + return; + } + + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + if (url.indexOf("$CD") != -1 && url.indexOf("$CD=") == -1) { + SendWS( + "[MSG: BeginData]\n" + + "board: Bart Board\n" + + "name: Default (Test Drive with SD)\n" + + "stepping:\n" + + " engine: RMT\n" + + " idle_ms: 255\n" + + " pulse_us: 4\n" + + " dir_delay_us: 0\n" + + " disable_delay_us: 0\n" + + "\n" + + "axes:\n" + + " shared_stepper_disable: NO_PIN\n" + + " x:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + " y:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + " z:\n" + + " steps_per_mm: 320.000\n" + + " max_rate: 1000.000\n" + + " acceleration: 25.000\n" + + " max_travel: 200.000\n" + + " soft_limits: false\n" + + "\n" + + "spi:\n" + + " miso: gpio.19\n" + + " mosi: gpio.23\n" + + " sck: gpio.18\n" + + "\n" + + "sdcard:\n" + + " cs: gpio.5\n" + + " card_detect: NO_PIN\n" + + "\n" + + "control:\n" + + " safety_door: NO_PIN\n" + + " reset: NO_PIN\n" + + " feed_hold: NO_PIN\n" + + " cycle_start: NO_PIN\n" + + " macro0: NO_PIN\n" + + " macro1: NO_PIN\n" + + " macro2: NO_PIN\n" + + " macro3: NO_PIN\n" + + "\n" + + "coolant:\n" + + " flood: NO_PIN\n" + + " mist: NO_PIN\n" + + " delay_ms: 0\n" + + "\n" + + "probe:\n" + + " pin: NO_PIN\n" + + " check_mode_start: true\n" + + "\n" + + "macros:\n" + + " n0: \n" + + " n1: \n" + + " macro0: \n" + + " macro1: \n" + + " macro2: \n" + + " macro3: \n" + + "\n" + + "user_outputs:\n" + + " analog0: NO_PIN\n" + + " analog1: NO_PIN\n" + + " analog2: NO_PIN\n" + + " analog3: NO_PIN\n" + + " analog_frequency0: 5000\n" + + " analog_frequency1: 5000\n" + + " analog_frequency2: 5000\n" + + " analog_frequency3: 5000\n" + + " digital0: NO_PIN\n" + + " digital1: NO_PIN\n" + + " digital2: NO_PIN\n" + + " digital3: NO_PIN\n" + + "\n" + + "software_debounce_ms: 0\n" + + "laser_mode: false\n" + + "arc_tolerance: 0.002\n" + + "junction_deviation: 0.010\n" + + "verbose_errors: false\n" + + "report_inches: false\n" + + "homing_init_lock: true\n" + + "enable_parking_override_control: false\n" + + "deactivate_parking_upon_init: false\n" + + "check_limits_at_init: true\n" + + "limits_two_switches_on_axis: false\n" + + "disable_laser_during_hold: true\n" + + "use_line_numbers: false\n" + + "NoSpindle:\n" + + "\n" + + "[MSG: EndData]\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("$Config/Filename") != -1) { + if (url.indexOf("$Config/Filename=") == -1) + SendWS("$Config/Filename=config.yaml\n"); + SendWS("ok\n"); + res.send(""); + return; + } + if (req.query.cmd && req.query.cmd == "?") { + countStatus++; + if (countStatus == 1) + SendWS( + "\n" + ); + if (countStatus == 2) + SendWS( + "\n" + ); + if (countStatus > 2) + SendWS("\n"); + if (countStatus == 10) countStatus = 0; + res.send(""); + return; + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "fluidnc", + FWTargetID: "60", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }); + return; + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111"); + return; + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "fluidnc" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }); + return; + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }); + return; + } + + if (url.indexOf("$$") != -1) { + SendWS( + "$0=3\n" + + "$1=250\n" + + "$2=0\n" + + "$3=0\n" + + "$4=0\n" + + "$5=1\n" + + "$6=0\n" + + "$10=1\n" + + "$11=0.010\n" + + "$12=0.002\n" + + "$13=0\n" + + "$20=0\n" + + "$21=0\n" + + "$22=0\n" + + "$23=3\n" + + "$24=200.000\n" + + "$25=2000.000\n" + + "$26=250\n" + + "$27=1.000\n" + + "$30=1000.000\n" + + "$31=0.000\n" + + "$32=0\n" + + "$100=100.000\n" + + "$101=100.000\n" + + "$102=100.000\n" + + "$103=100.000\n" + + "$104=100.000\n" + + "$105=100.000\n" + + "$110=1000.000\n" + + "$111=1000.000\n" + + "$112=1000.000\n" + + "$113=1000.000\n" + + "$114=1000.000\n" + + "$115=1000.000\n" + + "$120=200.000\n" + + "$121=200.000\n" + + "$122=200.000\n" + + "$123=200.000\n" + + "$124=200.000\n" + + "$125=200.000\n" + + "$130=300.000\n" + + "$131=300.000\n" + + "$132=300.000\n" + + "$133=300.000\n" + + "$134=300.000\n" + + "$135=300.000\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8); + SendWS(text, false); + return; + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [{ none: "0" }, { pushover: "1" }, { email: "2" }, { line: "3" }], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }); + return; + } + SendWS("ok\n"); + res.send(""); +}; + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401); + logindone = false; + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true; + lastconnection = Date.now(); + } else { + res.status(401); + logindone = false; + } + res.send(""); +}; + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: FluidNC v3.2.2
" + + "FW arch: ESP32 " + ); +}; + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +}; diff --git a/src/server/indexgrbl.js b/config/targets/SandTable/GRBL/index.js similarity index 54% rename from src/server/indexgrbl.js rename to config/targets/SandTable/GRBL/index.js index f3b3e6e8..6c2b0269 100644 --- a/src/server/indexgrbl.js +++ b/config/targets/SandTable/GRBL/index.js @@ -1,76 +1,159 @@ -const express = require("express") -var path = require("path") -const fs = require("fs") /* - * Web Server for development - * Web Socket server for development - */ + index.js - ESP3D WebUI Target file -const WebSocket = require("ws") -var currentID = 0 -const app = express() -const fileUpload = require("express-fileupload") -const machine = "grbl" + Copyright (c) 2020 Luc Lebosse. All rights reserved. -let queryInterval = null -let feedrate = 100 -let nbAxis = 6 + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. -let targetFW = machine + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. -const FSDir = "./public" -var WebSocketServer = require("ws").Server, - wss = new WebSocketServer({ port: 81 }) + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ -app.use(fileUpload({ preserveExtension: true, debug: true })) +const chalk = require("chalk") +const wscolor = chalk.cyan +const expresscolor = chalk.green +const commandcolor = chalk.white +const enableAuthentication = false +let lastconnection = Date.now() +let logindone = false +const sessiontTime = 60000 +let countStatus = 0 -/*function SendBinary16(text){ - var buf = new ArrayBuffer(text.length*2); - var bufView = new Uint16Array(buf); - for (var i=0, strLen=text.length; i 2) { - nbqueries = 0 - SendBinary( - "\n" - ) - } else { - SendBinary("\n") - nbqueries++ +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)) + else console.log(commandcolor(`[server]/command : ${url}`)) + if (url.indexOf("PING") != -1) { + lastconnection = Date.now() + res.status(200) + res.send("ok\n") + console.log(commandcolor(`[server]/command :PING`)) + return } -} -app.get("/command", function (req, res) { - var url = req.originalUrl - console.log(url) - if (url.indexOf("cmd=%3F") != -1) { - sendStatus() + + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + + if (req.query.cmd && req.query.cmd == "?") { + countStatus++ + if (countStatus == 1) + SendWS( + "\n" + ) + if (countStatus == 2) + SendWS( + "\n" + ) + if (countStatus > 2) + SendWS("\n") + if (countStatus == 10) countStatus = 0 res.send("") return } - if (url.indexOf("cmd=%24%24") != -1) { - SendBinary( + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "grbl", + FWTargetID: "10", + Setup: "Enabled", + SDConnection: "shared", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + CameraID: "4", + CameraName: "ESP32 Cam", + }, + }) + return + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111") + return + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "targetfw", value: "grbl" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }) + return + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }) + return + } + + if (url.indexOf("$$") != -1) { + SendWS( "$0=3\n" + "$1=250\n" + "$2=0\n" + @@ -123,99 +206,17 @@ app.get("/command", function (req, res) { return } - if (url.indexOf("ESP800") != -1) { - console.log(targetFW) - res.json({ - FWVersion: "3.0.0.a28", - FWTarget: "grbl-embedded", - SDConnection: "none", - Authentication: "Disabled", - WebCommunication: "Synchronous", - WebSocketIP: "localhost", - WebSocketPort: "81", - Hostname: "esp3d", - WiFiMode: "STA", - WebUpdate: "Enabled", - Filesystem: "SPIFFS", - Time: "none", - NbAxis: nbAxis, - }) - return - } - - if (url.indexOf("ESP401") != -1) { - console.log(url) - let p1 = url.indexOf("P%3D") - let p2 = url.indexOf("%20T%3D") - let p = url.substring(p1 + 4, p2) - - //res.status(500).send("error " + p) - res.send("ok " + p) - return - } - - if (url.indexOf("ESP420") != -1) { - res.json({ - Status: [ - { id: "chip id", value: "38078" }, - { id: "CPU Freq", value: "240 Mhz" }, - { id: "CPU Temp", value: "50.6 C" }, - { id: "free mem", value: "217.50 KB" }, - { id: "SDK", value: "v3.3.1-61-g367c3c09c" }, - { id: "flash size", value: "4.00 MB" }, - { id: "size for update", value: "1.87 MB" }, - { id: "FS type", value: "SPIFFS" }, - { id: "FS usage", value: "39.95 KB/169.38 KB" }, - { id: "baud", value: "115200" }, - { id: "sleep mode", value: "none" }, - { id: "wifi", value: "ON" }, - { id: "hostname", value: "esp3d" }, - { id: "HTTP port", value: "80" }, - { id: "Telnet port", value: "23" }, - { id: "Ftp ports", value: "21, 20, 55600" }, - { id: "sta", value: "ON" }, - { id: "mac", value: "30:AE:A4:21:BE:94" }, - { id: "SSID", value: "WIFI_OFFICE_B2G" }, - { id: "signal", value: "100 %" }, - { id: "phy mode", value: "11n" }, - { id: "channel", value: "2" }, - { id: "ip mode", value: "dhcp" }, - { id: "ip", value: "192.168.1.43" }, - { id: "gw", value: "192.168.1.1" }, - { id: "msk", value: "255.255.255.0" }, - { id: "DNS", value: "192.168.1.1" }, - { id: "ap", value: "OFF" }, - { id: "mac", value: "30:AE:A4:21:BE:95" }, - { id: "serial", value: "ON" }, - { id: "notification", value: "OFF" }, - { id: "FW ver", value: "3.0.0.a28" }, - { id: "FW arch", value: "ESP32" }, - ], - }) - return - } - - if (url.indexOf("ESP410") != -1) { - res.json({ - AP_LIST: [ - { - SSID: "HP-Setup>71-M277 LaserJet", - SIGNAL: "92", - IS_PROTECTED: "0", - }, - { SSID: "WIFI_OFFICE_B2G", SIGNAL: "88", IS_PROTECTED: "1" }, - { SSID: "NETGEAR70", SIGNAL: "66", IS_PROTECTED: "1" }, - { SSID: "WIFI_OFFICE_A2G", SIGNAL: "48", IS_PROTECTED: "1" }, - { SSID: "Livebox-EF01", SIGNAL: "20", IS_PROTECTED: "1" }, - { SSID: "orange", SIGNAL: "20", IS_PROTECTED: "0" }, - ], - }) + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8) + SendWS(text, false) return } if (url.indexOf("ESP400") != -1) { res.json({ - Settings: [ + cmd: "400", + status: "ok", + data: [ { F: "network/network", P: "130", @@ -232,6 +233,7 @@ app.get("/command", function (req, res) { V: "1", H: "radio mode", O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", }, { F: "network/sta", @@ -241,6 +243,7 @@ app.get("/command", function (req, res) { S: "32", H: "SSID", M: "1", + R: "1", }, { F: "network/sta", @@ -250,7 +253,9 @@ app.get("/command", function (req, res) { V: "********", S: "64", H: "pwd", - M: "8", + M: "0", + MS: "8", + R: "1", }, { F: "network/sta", @@ -259,6 +264,7 @@ app.get("/command", function (req, res) { V: "1", H: "ip mode", O: [{ dhcp: "1" }, { static: "0" }], + R: "1", }, { F: "network/sta", @@ -266,6 +272,7 @@ app.get("/command", function (req, res) { T: "A", V: "192.168.0.1", H: "ip", + R: "1", }, { F: "network/sta", @@ -273,6 +280,7 @@ app.get("/command", function (req, res) { T: "A", V: "192.168.0.1", H: "gw", + R: "1", }, { F: "network/sta", @@ -280,6 +288,7 @@ app.get("/command", function (req, res) { T: "A", V: "255.255.255.0", H: "msk", + R: "1", }, { F: "network/ap", @@ -289,6 +298,7 @@ app.get("/command", function (req, res) { S: "32", H: "SSID", M: "1", + R: "1", }, { F: "network/ap", @@ -298,7 +308,9 @@ app.get("/command", function (req, res) { V: "********", S: "64", H: "pwd", - M: "8", + M: "0", + MS: "8", + R: "1", }, { F: "network/ap", @@ -306,6 +318,7 @@ app.get("/command", function (req, res) { T: "A", V: "192.168.0.1", H: "ip", + R: "1", }, { F: "network/ap", @@ -329,6 +342,7 @@ app.get("/command", function (req, res) { { 13: "13" }, { 14: "14" }, ], + R: "1", }, { F: "service/http", @@ -449,133 +463,115 @@ app.get("/command", function (req, res) { }, { F: "system/system", - P: "129", - T: "F", - V: "255", - H: "outputmsg", - O: [{ M117: "16" }, { serial: "1" }, { telnet: "2" }], + P: "461", + T: "B", + V: "10", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", }, ], }) return } - SendBinary("ok\n") - res.json({ custom: "unknown query" }) -}) - -function fileSizeString(size) { - let s - if (size < 1024) return size + " B" - if (size < 1024 * 1024) return (size / 1024).toFixed(2) + " KB" - if (size < 1024 * 1024 * 1024) - return (size / (1024 * 1024)).toFixed(2) + " MB" - if (size < 1024 * 1024 * 1024 * 1024) - return (size / (1024 * 1024 * 1024)).toFixed(2) + " GB" - return "X B" + SendWS("ok\n") + res.send("") } -function filesList(mypath) { - let res = '{"files":[' - let nb = 0 - let totalused = getTotalSize(__dirname + "/public") - let total = 1.31 * 1024 * 1024 - fs.readdirSync(__dirname + "/public" + mypath).forEach((fileelement) => { - let fst = fs.statSync(__dirname + "/public" + mypath + fileelement) - let fsize = -1 - - if (fst.isFile()) { - fsize = fileSizeString(fst.size) - } - if (nb > 0) res += "," - res += '{"name":"' + fileelement + '","size":"' + fsize + '"}' - nb++ - }) - res += - '],"path":"' + - mypath + - '","occupation":"' + - (totalused / total).toFixed(0) + - '","status":"ok","total":"' + - fileSizeString(total) + - '","used":"' + - fileSizeString(totalused) + - '"}' - return res +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401) + logindone = false + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true + lastconnection = Date.now() + } else { + res.status(401) + logindone = false + } + res.send("") } -const getAllFiles = function (dirPath, arrayOfFiles) { - let files = fs.readdirSync(dirPath) - - arrayOfFiles = arrayOfFiles || [] - - files.forEach(function (file) { - if (fs.statSync(dirPath + "/" + file).isDirectory()) { - arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles) - } else { - arrayOfFiles.push(dirPath + "/" + file) - } - }) - - return arrayOfFiles +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401) + return + } + lastconnection = Date.now() + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: grbl
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ) } -const getTotalSize = function (directoryPath) { - const arrayOfFiles = getAllFiles(directoryPath) - - let totalSize = 0 - - arrayOfFiles.forEach(function (filePath) { - totalSize += fs.statSync(filePath).size - }) - - return totalSize +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, } - -app.all("/updatefw", function (req, res) { - res.send("ok") -}) - -app.all("/files", function (req, res) { - let mypath = req.query.path - if (typeof mypath == "undefined") mypath = "/" - if (!req.files || Object.keys(req.files).length === 0) { - return res.send(filesList(mypath)) - } - let myFile = req.files.myfile - - if (typeof myFile.length == "undefined") { - console.log("one files") - myFile.mv(__dirname + "/public" + mypath + myFile.name, function (err) { - if (err) return res.status(500).send(err) - }) - } else { - console.log(myFile.length + " files") - for (let i = 0; i < myFile.length; i++) { - myFile[i].mv( - __dirname + "/public/" + myFile[i].name, - function (err) { - if (err) return res.status(500).send(err) - } - ) - } - } - res.send(filesList(mypath)) -}) - -app.listen(process.env.PORT || 8080, () => - console.log(`Listening on port ${process.env.PORT || 8080}!`) -) - -wss.on("connection", function (ws) { - console.log("New connection") - ws.send(`currentID:${currentID}`) - wss.clients.forEach(function each(client) { - if (client.readyState === WebSocket.OPEN) { - client.send(`activeID:${currentID}`) - } - }) - currentID++ - ws.on("message", function (message) { - console.log("received: %s", message) - }) -}) diff --git a/config/targets/SandTable/Marlin/index.js b/config/targets/SandTable/Marlin/index.js new file mode 100644 index 00000000..5ec17c1c --- /dev/null +++ b/config/targets/SandTable/Marlin/index.js @@ -0,0 +1,621 @@ +/* + index.js - ESP3D WebUI Target file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +const chalk = require("chalk"); +const wscolor = chalk.cyan; +const expresscolor = chalk.green; +const commandcolor = chalk.white; +const enableAuthentication = false; +let lastconnection = Date.now(); +let logindone = false; +const sessiontTime = 60000; + +function getLastconnection() { + return lastconnection; +} + +function hasEnabledAuthentication() { + return enableAuthentication; +} + +const commandsQuery = (req, res, SendWS) => { + let url = req.query.cmd ? req.query.cmd : req.originalUrl; + if (req.query.cmd) + console.log(commandcolor(`[server]/command params: ${req.query.cmd}`)); + else console.log(commandcolor(`[server]/command : ${url}`)); + if (url.indexOf("PING") != -1) { + lastconnection = Date.now(); + res.status(200); + res.send("ok\n"); + console.log(commandcolor(`[server]/command :PING`)); + return; + } + + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + + if (url.indexOf("M114") != -1) { + let X = Number(Math.random() * 200.12).toFixed(2); + let Y = Number(Math.random() * 200.12).toFixed(2); + let Z = Number(Math.random() * 200.12).toFixed(2); + SendWS(`X:${X} Y:${Y} Z:${Z} E:0.00 Count X: 0 Y:10160 Z:116000\nok\n`); + res.send(""); + return; + } + + if (url.indexOf("M20") != -1) { + SendWS( + "Begin file list\n" + + "CUBE2.GCO 210240\n" + + "CUBE01.GCO 2089832\n" + + "SUPPORT2.GCO 4613256\n" + + "ARCHIVE/CUBE.GCO 210240\n" + + "ARCHIVE/CUBE-C~1.GCO 210240\n" + + "NEWFOL~1/SUPPORT2.GCO 4613256\n" + + "End file list\n" + + "ok\n" + ); + /* SendWS( + "Begin file list\n" + + "COOL_V~1.GCO 66622272\n" + + "415%VA~1.GCO 66622272\n" + + "/ARCHIEVE/SUBDIR/TWISTY~1.GCO 1040\n" + + "/ARCHIEVE/STEEL-~1.GCO 2040\n" + + "/ARCHIEVE/STEEL_~1.GCO 2040\n" + + "/ARCHIEVE/RET229~1.GCO 2050\n" + + "/ARCHIEVE/FILE__~1.GCO 1050\n" + + "/ARCHIEVE/FILE__~2.GCO 1050\n" + + "/ARCHIEVE/FILE__~3.GCO 1050\n" + + "/ARCHIEVE/FILE__~4.GCO 1050\n" + + "/ARCHIEVE/FILE__~5.GCO 1050\n" + + "End file list\n" + + "ok\n" + );*/ + res.send(""); + return; + } + + if (url.indexOf("M30") != -1) { + const name = url.split(" "); + SendWS( + //"Deletion failed, File:" + name[1].substring(1) + ".\n" + "ok\n" + "File deleted:" + name[1].substring(1) + "\n" + "ok\n" + ); + + res.send(""); + return; + } + if (url.indexOf("M115") != -1) { + SendWS( + "FIRMWARE_NAME:Marlin 2.0.9.1 (Sep 8 2021 17:07:06) SOURCE_CODE_URL:github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:MRR ESPA EXTRUDER_COUNT:1 UUID:cede2a2f-41a2-4748-9b12-c55c62f367ff\n" + + "Cap:SERIAL_XON_XOFF:0\n" + + "Cap:BINARY_FILE_TRANSFER:0\n" + + "Cap:EEPROM:0\n" + + "Cap:VOLUMETRIC:1\n" + + "Cap:AUTOREPORT_POS:0\n" + + "Cap:AUTOREPORT_TEMP:1\n" + + "Cap:PROGRESS:0\n" + + "Cap:PRINT_JOB:1\n" + + "Cap:AUTOLEVEL:0\n" + + "Cap:RUNOUT:0\n" + + "Cap:Z_PROBE:0\n" + + "Cap:LEVELING_DATA:0\n" + + "Cap:BUILD_PERCENT:0\n" + + "Cap:SOFTWARE_POWER:0\n" + + "Cap:TOGGLE_LIGHTS:0\n" + + "Cap:CASE_LIGHT_BRIGHTNESS:0\n" + + "Cap:EMERGENCY_PARSER:0\n" + + "Cap:HOST_ACTION_COMMANDS:0\n" + + "Cap:PROMPT_SUPPORT:0\n" + + "Cap:SDCARD:1\n" + + "Cap:REPEAT:0\n" + + "Cap:SD_WRITE:1\n" + + "Cap:AUTOREPORT_SD_STATUS:0\n" + + "Cap:LONG_FILENAME:1\n" + + "Cap:THERMAL_PROTECTION:1\n" + + "Cap:MOTION_MODES:0\n" + + "Cap:ARCS:1\n" + + "Cap:BABYSTEPPING:0\n" + + "Cap:CHAMBER_TEMPERATURE:0\n" + + "Cap:COOLER_TEMPERATURE:0\n" + + "Cap:MEATPACK:0\n" + + "ok\n" + ); + res.send(""); + return; + } + if (url.indexOf("M503") != -1) { + SendWS( + "echo: G21 ; Units in mm (mm)\n" + + " \n" + + "echo:; Filament settings: Disabled\n" + + "echo: M200 S0 D1.75\n" + + "echo:; Steps per unit:\n" + + "echo: M92 X80.00 Y80.00 Z400.00 E500.00\n" + + "echo:; Maximum feedrates (units/s):\n" + + "echo: M203 X300.00 Y300.00 Z5.00 E25.00\n" + + "echo:; Maximum Acceleration (units/s2):\n" + + "echo: M201 X3000.00 Y3000.00 Z100.00 E10000.00\n" + + "echo:; Acceleration (units/s2): P R T\n" + + "echo: M204 P3000.00 R3000.00 T3000.00\n" + + "echo:; Advanced: B S T J\n" + + "echo: M205 B20000.00 S0.00 T0.00 J0.01\n" + + "echo:; Home offset:\n" + + "echo: M206 X0.00 Y0.00 Z0.00\n" + + "echo:; PID settings:\n" + + "echo: M301 P22.20 I1.08 D114.00\n" + + "ok\n" + ); + res.send(""); + return; + } + + if (url.indexOf("M105") != -1) { + SendWS(Temperatures()); + res.send(""); + return; + } + + if (url.indexOf("ESP800") != -1) { + res.json({ + cmd: "800", + status: "ok", + data: { + FWVersion: "3.0.0.a111", + FWTarget: "marlin", + FWTargetID: "40", + Setup: "Enabled", + SDConnection: "none", + SerialProtocol: "Socket", + Authentication: "Disabled", + WebCommunication: "Synchronous", + WebSocketIP: "localhost", + WebSocketPort: "81", + Hostname: "esp3d", + WiFiMode: "STA", + WebUpdate: "Enabled", + FileSystem: "LittleFS", + Time: "none", + }, + }); + return; + } + if (url.indexOf("ESP111") != -1) { + res.send("192.168.1.111"); + return; + } + if (url.indexOf("ESP420") != -1) { + res.json({ + cmd: "420", + status: "ok", + data: [ + { id: "chip id", value: "18569" }, + { id: "CPU Freq", value: "240Mhz" }, + { id: "CPU Temp", value: "54.4C" }, + { id: "free mem", value: "201.86 KB" }, + { id: "SDK", value: "v4.4-beta1-308-gf3e0c8bc41" }, + { id: "flash size", value: "4.00 MB" }, + { id: "size for update", value: "1.25 MB" }, + { id: "FS type", value: "LittleFS" }, + { id: "FS usage", value: "64.00 KB/1.44 MB" }, + { id: "sleep mode", value: "none" }, + { id: "wifi", value: "ON" }, + { id: "hostname", value: "esp3d" }, + { id: "HTTP port", value: "80" }, + { id: "Telnet port", value: "23" }, + { id: "sta", value: "ON" }, + { id: "mac", value: "24:6F:28:4C:89:48" }, + { id: "SSID", value: "luc-ext1" }, + { id: "signal", value: "60%" }, + { id: "phy mode", value: "11n" }, + { id: "channel", value: "3" }, + { id: "ip mode", value: "dhcp" }, + { id: "ip", value: "192.168.2.215" }, + { id: "gw", value: "192.168.2.1" }, + { id: "msk", value: "255.255.255.0" }, + { id: "DNS", value: "192.168.2.1" }, + { id: "ap", value: "OFF" }, + { id: "mac", value: "24:6F:28:4C:89:49" }, + { id: "notification", value: "ON(line)" }, + { id: "sd", value: "shared(SDFat - 2.1.2)" }, + { id: "targetfw", value: "marlin" }, + { id: "FW ver", value: "3.0.0.a111" }, + { id: "FW arch", value: "ESP32" }, + ], + }); + return; + } + + if (url.indexOf("ESP410") != -1) { + res.json({ + cmd: "410", + status: "ok", + data: [{ SSID: "luc-ext1", SIGNAL: "52", IS_PROTECTED: "1" }], + }); + return; + } + + if (url.indexOf("ESP600") != -1) { + const text = url.substring(8); + SendWS(text, false); + return; + } + + if (url.indexOf("ESP400") != -1) { + res.json({ + cmd: "400", + status: "ok", + data: [ + { + F: "network/network", + P: "130", + T: "S", + V: "esp3d", + H: "hostname", + S: "32", + M: "1", + }, + { + F: "network/network", + P: "0", + T: "B", + V: "1", + H: "radio mode", + O: [{ none: "0" }, { sta: "1" }, { ap: "2" }], + R: "1", + }, + { + F: "network/sta", + P: "1", + T: "S", + V: "WIFI_OFFICE_B2G", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/sta", + P: "34", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/sta", + P: "99", + T: "B", + V: "1", + H: "ip mode", + O: [{ dhcp: "1" }, { static: "0" }], + R: "1", + }, + { + F: "network/sta", + P: "100", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/sta", + P: "108", + T: "A", + V: "192.168.0.1", + H: "gw", + R: "1", + }, + { + F: "network/sta", + P: "104", + T: "A", + V: "255.255.255.0", + H: "msk", + R: "1", + }, + { + F: "network/ap", + P: "218", + T: "S", + V: "ESP3D", + S: "32", + H: "SSID", + M: "1", + R: "1", + }, + { + F: "network/ap", + P: "251", + T: "S", + N: "1", + V: "********", + S: "64", + H: "pwd", + M: "0", + MS: "8", + R: "1", + }, + { + F: "network/ap", + P: "316", + T: "A", + V: "192.168.0.1", + H: "ip", + R: "1", + }, + { + F: "network/ap", + P: "118", + T: "B", + V: "11", + H: "channel", + O: [ + { 1: "1" }, + { 2: "2" }, + { 3: "3" }, + { 4: "4" }, + { 5: "5" }, + { 6: "6" }, + { 7: "7" }, + { 8: "8" }, + { 9: "9" }, + { 10: "10" }, + { 11: "11" }, + { 12: "12" }, + { 13: "13" }, + { 14: "14" }, + ], + R: "1", + }, + { + F: "service/http", + P: "328", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/http", + P: "121", + T: "I", + V: "80", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/telnetp", + P: "329", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/telnetp", + P: "125", + T: "I", + V: "23", + H: "port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1021", + T: "B", + V: "1", + H: "enable", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/ftp", + P: "1009", + T: "I", + V: "21", + H: "control port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1013", + T: "I", + V: "20", + H: "active port", + S: "65001", + M: "1", + }, + { + F: "service/ftp", + P: "1017", + T: "I", + V: "55600", + H: "passive port", + S: "65001", + M: "1", + }, + { + F: "service/notification", + P: "1004", + T: "B", + V: "1", + H: "auto notif", + O: [{ no: "0" }, { yes: "1" }], + }, + { + F: "service/notification", + P: "116", + T: "B", + V: "0", + H: "notification", + O: [{ none: "0" }, { pushover: "1" }, { email: "2" }, { line: "3" }], + }, + { + F: "service/notification", + P: "332", + T: "S", + V: "********", + S: "63", + H: "t1", + M: "0", + }, + { + F: "service/notification", + P: "396", + T: "S", + V: "********", + S: "63", + H: "t2", + M: "0", + }, + { + F: "service/notification", + P: "855", + T: "S", + V: " ", + S: "127", + H: "ts", + M: "0", + }, + { + F: "system/system", + P: "461", + T: "B", + V: "40", + H: "targetfw", + O: [ + { repetier: "50" }, + { marlin: "20" }, + { smoothieware: "40" }, + { grbl: "10" }, + { unknown: "0" }, + ], + }, + { + F: "system/system", + P: "112", + T: "I", + V: "115200", + H: "baud", + O: [ + { 9600: "9600" }, + { 19200: "19200" }, + { 38400: "38400" }, + { 57600: "57600" }, + { 74880: "74880" }, + { 115200: "115200" }, + { 230400: "230400" }, + { 250000: "250000" }, + { 500000: "500000" }, + { 921600: "921600" }, + ], + }, + { + F: "system/system", + P: "320", + T: "I", + V: "10000", + H: "bootdelay", + S: "40000", + M: "0", + }, + ], + }); + return; + } + SendWS("ok\n"); + res.send(""); +}; + +const loginURI = (req, res) => { + if (req.body.DISCONNECT == "Yes") { + res.status(401); + logindone = false; + } else if (req.body.USER == "admin" && req.body.PASSWORD == "admin") { + logindone = true; + lastconnection = Date.now(); + } else { + res.status(401); + logindone = false; + } + res.send(""); +}; + +const configURI = (req, res) => { + if (!logindone && enableAuthentication) { + res.status(401); + return; + } + lastconnection = Date.now(); + res.send( + "chip id: 56398\nCPU Freq: 240 Mhz
" + + "CPU Temp: 58.3 C
" + + "free mem: 212.36 KB
" + + "SDK: v3.2.3-14-gd3e562907
" + + "flash size: 4.00 MB
" + + "size for update: 1.87 MB
" + + "FS type: LittleFS
" + + "FS usage: 104.00 KB/192.00 KB
" + + "baud: 115200
" + + "sleep mode: none
" + + "wifi: ON
" + + "hostname: esp3d
" + + "HTTP port: 80
" + + "Telnet port: 23
" + + "WebDav port: 8383
" + + "sta: ON
" + + "mac: 80:7D:3A:C4:4E:DC
" + + "SSID: WIFI_OFFICE_A2G
" + + "signal: 100 %
" + + "phy mode: 11n
" + + "channel: 11
" + + "ip mode: dhcp
" + + "ip: 192.168.1.61
" + + "gw: 192.168.1.1
" + + "msk: 255.255.255.0
" + + "DNS: 192.168.1.1
" + + "ap: OFF
" + + "mac: 80:7D:3A:C4:4E:DD
" + + "serial: ON
" + + "notification: OFF
" + + "Target Fw: marlin
" + + "FW ver: 3.0.0.a91
" + + "FW arch: ESP32 " + ); +}; + +module.exports = { + commandsQuery, + configURI, + loginURI, + getLastconnection, + hasEnabledAuthentication, +}; diff --git a/config/webpack.dev.js b/config/webpack.dev.js new file mode 100644 index 00000000..8c643e5e --- /dev/null +++ b/config/webpack.dev.js @@ -0,0 +1,87 @@ +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +let target = process.env.TARGET_ENV ? process.env.TARGET_ENV : "Printer3D"; +let subtarget = process.env.SUBTARGET_ENV + ? process.env.SUBTARGET_ENV + : "Marlin"; +console.log("Target:", target, " Subtarget:", subtarget); +module.exports = { + resolve: { + alias: { + TargetDir: path.resolve(__dirname, "../src/targets", target), + SubTargetDir: path.resolve( + __dirname, + "../src/targets", + target, + subtarget + ), + }, + }, + mode: "development", // this will trigger some webpack default stuffs for dev + entry: path.resolve(__dirname, "../src/index.js"), // if not set, default path to './src/index.js'. Accepts an object with multiple key-value pairs, with key as your custom bundle filename(substituting the [name]), and value as the corresponding file path + output: { + filename: "[name].bundle.js", // [name] will take whatever the input filename is. defaults to 'main' if only a single entry value + path: path.resolve(__dirname, "../dist"), // the folder containing you final dist/build files. Default to './dist' + }, + devServer: { + historyApiFallback: true, // to make our SPA works after a full reload, so that it serves 'index.html' when 404 response + open: true, + static: { + directory: path.resolve(__dirname, "server", "public"), + }, + port: 8088, + proxy: { + context: () => true, + target: "http://localhost:8080", + }, + }, + stats: "minimal", // default behaviour spit out way too much info. adjust to your need. + devtool: "source-map", // a sourcemap type. map to original source with line number + plugins: [ + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // all options are optional + filename: "[name].css", + chunkFilename: "[id].css", + ignoreOrder: false, // Enable to remove warnings about conflicting order + }), + new HtmlWebpackPlugin({ + template: path.join(__dirname, "../src/index.html"), + inlineSource: ".(js|css)$", + inject: true, + }), + ], // automatically creates a 'index.html' for us with our , \ No newline at end of file diff --git a/server/public/index.html.gz b/server/public/index.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..3f33cfa44b8ba203c2320a20d7726c084ed099f7 GIT binary patch literal 38371 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 T{*3bJl&Wyo&03S t+Go?+m2@DI!G>U|XKNQfowMS_-n~y-m+I;1DRI?u0RV*RS7|-~006SpD1HC{ literal 0 HcmV?d00001 diff --git a/server/public/modal.html b/server/public/modal.html new file mode 100644 index 00000000..4d2f49f7 --- /dev/null +++ b/server/public/modal.html @@ -0,0 +1,32 @@ + + +
+ + + + +
+
+
+ diff --git a/server/public/my config.yaml b/server/public/my config.yaml new file mode 100644 index 00000000..214921a2 --- /dev/null +++ b/server/public/my config.yaml @@ -0,0 +1,96 @@ +board: None +name: Default (Test Drive) +stepping: + engine: RMT + idle_ms: 255 + pulse_us: 4 + dir_delay_us: 0 + disable_delay_us: 0 + +axes: + shared_stepper_disable: NO_PIN + x: + steps_per_mm: 320.000 + max_rate: 1000.000 + acceleration: 25.000 + max_travel: 200.000 + soft_limits: false + + y: + steps_per_mm: 320.000 + max_rate: 1000.000 + acceleration: 25.000 + max_travel: 200.000 + soft_limits: false + + z: + steps_per_mm: 320.000 + max_rate: 1000.000 + acceleration: 25.000 + max_travel: 200.000 + soft_limits: false + +spi: + miso: NO_PIN + mosi: NO_PIN + sck: NO_PIN + +control: + safety_door: NO_PIN + reset: NO_PIN + feed_hold: NO_PIN + cycle_start: NO_PIN + macro0: NO_PIN + macro1: NO_PIN + macro2: NO_PIN + macro3: NO_PIN + +coolant: + flood: NO_PIN + mist: NO_PIN + delay_ms: 0 + +probe: + pin: NO_PIN + check_mode_start: true + +macros: + n0: + n1: + macro0: + macro1: + macro2: + macro3: + +user_outputs: + analog0: NO_PIN + analog1: NO_PIN + analog2: NO_PIN + analog3: NO_PIN + analog_frequency0: 5000 + analog_frequency1: 5000 + analog_frequency2: 5000 + analog_frequency3: 5000 + digital0: NO_PIN + digital1: NO_PIN + digital2: NO_PIN + digital3: NO_PIN + +sdcard: + card_detect: NO_PIN + cs: gpio.5 + +software_debounce_ms: 0 +laser_mode: false +arc_tolerance: 0.002 +junction_deviation: 0.010 +verbose_errors: false +report_inches: false +homing_init_lock: true +enable_parking_override_control: false +deactivate_parking_upon_init: false +check_limits_at_init: true +limits_two_switches_on_axis: false +disable_laser_during_hold: true +use_line_numbers: false +NoSpindle: diff --git a/server/public/preferences.json b/server/public/preferences.json new file mode 100644 index 00000000..9c852735 --- /dev/null +++ b/server/public/preferences.json @@ -0,0 +1,59 @@ +{ + "settings": { + "language": "default", + "theme": "default", + "autoload": true, + "mobileview": false, + "audio": false, + "showsensorspanel": false, + "enablepolling": false, + "pollingrefresh": "3000", + "pollingcommands": "M105", + "showterminalpanel": true, + "openterminalonstart": false, + "verbose": true, + "autoscroll": true, + "showfilespanel": true, + "openfilesonstart": false, + "filesfilter": "g;G;gco;GCO;gcode;GCODE", + "tftsd": false, + "tftusb": false, + "sd": false, + "showjogpanel": true, + "openjogonstart": false, + "xyfeedrate": "1000", + "zfeedrate": "1000", + "xpos": "100", + "ypos": "100", + "zpos": "100", + "homecmd": "G28 $", + "motoroff": "M84", + "emergencystop": "M112", + "showmacrospanel": true, + "openmacrosonstart": false, + "macros": [], + "showextracontents": false, + "openextrapanelsonstart": false, + "extracontents": [], + "showtemperaturespanel": true, + "opentemperaturesonstart": true, + "extrudermax": "300", + "extruderpreheat": "190;220;230", + "bedmax": "140", + "bedpreheat": "50;90;110", + "chambermax": "140", + "chamberpreheat": "50;90;110", + "showextruderspanel": true, + "openextrudersonstart": true, + "ismixedextruder": false, + "ecolors": "blue;red;green;#FF00FF", + "efeedrate": "400", + "enumber": "1", + "showfanspanel": true, + "openfansonstart": true, + "showspeedpanel": false, + "openspeedonstart": true, + "showflowratepanel": false, + "openflowrateonstart": true + } +} \ No newline at end of file diff --git a/server/public/query.html b/server/public/query.html new file mode 100644 index 00000000..8931c08a --- /dev/null +++ b/server/public/query.html @@ -0,0 +1,33 @@ + + +
+ +
+
+
+ diff --git a/server/public/sound.html b/server/public/sound.html new file mode 100644 index 00000000..dc5c70d7 --- /dev/null +++ b/server/public/sound.html @@ -0,0 +1,14 @@ + + +
+ + + + +
+ diff --git a/server/public/terminal.html b/server/public/terminal.html new file mode 100644 index 00000000..d6598da3 --- /dev/null +++ b/server/public/terminal.html @@ -0,0 +1,49 @@ + + +
+ +
+ + +
+
+

+    
+
+ diff --git a/server/public/theme-dark.gz b/server/public/theme-dark.gz new file mode 100644 index 0000000000000000000000000000000000000000..8b47353b3dc82759fced70ffe9490b612f4bb588 GIT binary patch literal 272 zcmV+r0q_1FiwFqT6|P_a19WI*ZDlQFVRCB#jZn*O!ypj6^A)agH7HV4_4PH2S=%VU zBD{7IMfvxFP1~fsR0u5$v&+n=9)U+p(smSN-V8QiYNlmq#S|eZ8{*7zNHj+Y#!#qu zPsz*mu@$vU=&N%cQ@|xq;AZtYb W_qyv}`q%#L@&8YhMgO|*0RRBiG=>KN literal 0 HcmV?d00001 diff --git a/server/public/theme-purple b/server/public/theme-purple new file mode 100644 index 00000000..e3740807 --- /dev/null +++ b/server/public/theme-purple @@ -0,0 +1,41 @@ +.feather-icon-container svg, +span.input-group-addon, +span.form-input, +input, +select, +span.text-dark, +label.text-dark, +label.hide-low, +section, +button, +.navbar, +.modal-container, +body, +.empty, +html{ +background-color:rgb(200, 200, 233)!important; +color:white!important; + +} + +a svg.esp3dlogo{ +color:#c0c0c0!important; +} + +.form-switch input:checked + .form-icon { + background-color:white!important; +} + +a.active svg, +a.active label.hide-low{ +color:blue!important; +} + +.navbar{ +border-bottom:white solid 0.5px; +} + +span.navbar-section{ +background-color:#0e0e6d!important; +color:white!important; +} diff --git a/server/public/toast.html b/server/public/toast.html new file mode 100644 index 00000000..7b1db10a --- /dev/null +++ b/server/public/toast.html @@ -0,0 +1,14 @@ + + +
+ + + + +
+ diff --git a/server/public/translate.html b/server/public/translate.html new file mode 100644 index 00000000..95321352 --- /dev/null +++ b/server/public/translate.html @@ -0,0 +1,34 @@ + + +
+ + +
+
+
+ diff --git a/server/public/upload.html b/server/public/upload.html new file mode 100644 index 00000000..d54846f3 --- /dev/null +++ b/server/public/upload.html @@ -0,0 +1,45 @@ + + +
+ + +
+
+
+ diff --git a/src/languages/zh_cn.json b/server/public/zh_cn.json similarity index 100% rename from src/languages/zh_cn.json rename to server/public/zh_cn.json diff --git a/sizetracker.txt b/sizetracker.txt index 87669a48..26b52672 100644 --- a/sizetracker.txt +++ b/sizetracker.txt @@ -1,35 +1,23 @@ - printer grbl -Base code : 0.0.0.1 : 25KB | -Websocket : 0.0.0.2 : 27KB | -Setup : 0.0.0.3 : 29KB | -Logo : 0.0.0.3 : 31.3KB | -Basic Navbar : 0.0.0.4 : 32.4KB | -Basic About : 0.0.0.5 : 34.15KB | -UI update : 0.0.0.6 : 35.7KB | -Settings Page : 0.0.0.7 : 36.3KB | -Basic ESP3D Settings : 0.0.0.8 : 36.9KB | -ESP3D Settings V1 : 0.0.0.9 : 39.6KB | -Scan network dialog : 0.0.0.11: 40.3KB | -Export Settings : 0.0.0.12: 40.6KB | -Import Settings : 0.0.0.13: 41.2KB | -Basic UI settings : 0.0.0.15: 41.8KB | -UI settings feedback : 0.0.0.16: 42.0KB | -UI language pack : 0.0.0.17: 42.6KB | -Machine settings : 0.0.0.18: 46.3KB | 45.6 KB -Terminal panel : 0.0.0.18: 47.4KB | 46.7 KB -Jog Panel : 0.0.0.21: 56.5KB | TBD -Jog Panel GRBL/Printer : 0.0.0.22: 56.7KB | 52.6KB -Code Refactoring : 0.0.0.23: 56.8KB | 52KB -Files Panel : 0.0.0.26: 60.0KB | N/A -Notification Panel : 0.0.0.44: 64.1KB | N/A -Feedrate / Flowrate : 0.0.0.47: 66.0KB | N/A -Temperatures controls : 0.0.0.53: 70.6KB | N/A -Extrusion : 0.0.0.58: 75.1KB | N/A -Temperatures charts : 0.0.0.59: 81.3KB | N/A -Sensors : 0.0.0.60: 83.0KB | N/A -Status : 0.0.0.61: 83.6KB | N/A -Macros : 0.0.0.66: 91.0KB | N/A -Authentication : 0.0.0.70: 91.2KB | N/A -Custom panels + camera : 0.0.0.71: 93.0KB | N/A -Custom pages : 0.0.0.73: 93.5KB | N/A + printer(Marlin) grbl size in 3.0 (Marlin) +Base code : 3.0.0.a1 : 21.2KB | | 27KB +WS/Http/Auth/Menu : 3.0.0.a2 : 28.4KB | | 34.15KB +Icons & Mobile view : 3.0.0.a3 : 29.8KB | | +UI update : 3.0.0.a4 : 30.6KB | | 35.7KB +Features : 3.0.0.a5 : 36KB | | 41.2KB +Interface : 3.0.0.a6 : 36.6KB | | 42KB +UI language pack : 3.0.0.a7 : 37.3KB | | 42.6KB +Theme pack : 3.0.0.a8 : 37.5KB | | N/A +Extra Panels / Macros / : : | | +Terminal : 3.0.0.a.12 : 50.4KB | | 42.6KB + 10.2KB + 1.1KB = 53.9KB +File panel : 3.0.0.a.13 : 53.3KB | | 53.9KB + 3.2KB = 57.1KB +Code Refactoring : 3.0.0.a.14 : 53.0KB | | 53.9KB + 3.2KB = 57.1KB +Machine Settings + Audio : 3.0.0.a.15 : 54.7KB | | 57.1KB + 3.7KB = 58.8KB +Machine Settings(1 btn save): 3.0.0.a.16 : 55.0KB | | 58.8KB +Jog Panel : 3.0.0.a.17 : 61.2KB | | 58.8KB+9.4KB = 68.1KB +Temperature Panel + : : | | +notifications : : 68.6KB | | 68.1 + 4.1 + 4.6= 76.8KB +Extruders Panel : : 70.2KB | | 76.8 + 4.5 = 81.3KB +Charts Panel : : 75.0KB | | 81.3 + 6.2 = 87.5KB +Extra controls Panel : : 78.3KB | | 87.5 + 3.6 = 91.1KB +Print status : : 78.5KB | | 91.1 + 0.6 = 91.4kb diff --git a/src/adapters/httpAdapter.js b/src/adapters/httpAdapter.js new file mode 100644 index 00000000..9bf81202 --- /dev/null +++ b/src/adapters/httpAdapter.js @@ -0,0 +1,120 @@ +/* + httpAdapter.js - ESP3D WebUI adapter file + + Copyright (c) 2021 Alexandre Aussourd. All rights reserved. + Modified by Luc LEBOSSE 2021 + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * Execute XMLHttpRequest + * @param {string} url + * @param {Object} params + * @returns {Object} + * @return {{a: number, b: string, c}} myObj + * @return {Object} return + * @returns {Promise} return.response + * @returns {Function} return.abort + * @returns {XHR} return.xhr + */ +const httpAdapter = (url, params = {}, setUploadProgress = () => {}) => { + const { method = "GET", headers = {}, body = null, id = null } = params + const sanitizedMethod = method.trim().toUpperCase() + const xhr = new XMLHttpRequest() + if (id && id.startsWith("download")) { + xhr.responseType = "blob" + xhr.addEventListener("progress", (e) => { + const done = e.position || e.loaded + const total = e.totalSize || e.total + const perc = Math.floor((done / total) * 1000) / 10 + setUploadProgress(perc) + }) + } else { + xhr.upload.addEventListener("progress", (e) => { + const done = e.position || e.loaded + const total = e.totalSize || e.total + const perc = Math.floor((done / total) * 1000) / 10 + setUploadProgress(perc) + }) + } + + const cacheBustedUrl = (url) => { + const parsedUrl = new URL(url) + let params = parsedUrl.searchParams + params.get("t") == null && params.append("t", Date.now()) + return parsedUrl.toString() + } + + xhr.open(sanitizedMethod, cacheBustedUrl(url), true) //Bypassing the cache + + /** handle URL params ? */ + + /** header part */ + if (headers instanceof Headers) + headers.forEach((value, header) => xhr.setRequestHeader(header, value)) + //handle Headers() + else + Object.entries(headers).forEach((header, value) => + xhr.setRequestHeader(header, value) + ) //handle Object headers + + const response = new Promise((resolve, reject) => { + xhr.onload = () => { + if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response) + else { + const e = new Error( + `${xhr.status ? xhr.status : ""}${ + xhr.statusText ? ` - ${xhr.statusText}` : "" + }` + ) + e.code = xhr.status + reject(e) + } + } + xhr.onerror = () => { + const e = new Error( + `${xhr.status ? xhr.status : "Connection time out"}${ + xhr.status && xhr.statusText ? ` - ${xhr.statusText}` : "" + }` + ) + e.code = xhr.status + reject(e) + } + + xhr.onabort = () => { + const e = new Error("Request aborted") + e.code = 499 + reject(e) + } + }) + + const sendBody = ["POST", "PUT", "CONNECT", "PATCH"].toLocaleString(method) + ? body + : null + + xhr.send(sendBody) + + return { + abort: (cb) => { + xhr.abort() + if (typeof callback == "function") return cb() + }, + xhr, + response, + } +} + +export { httpAdapter } diff --git a/src/adapters/index.js b/src/adapters/index.js new file mode 100644 index 00000000..65860c04 --- /dev/null +++ b/src/adapters/index.js @@ -0,0 +1,24 @@ +/* + index.js - ESP3D WebUI adapter file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +import { h } from "preact" + +import { httpAdapter } from "./httpAdapter" + +export { httpAdapter } diff --git a/src/areas/connection.js b/src/areas/connection.js new file mode 100644 index 00000000..4704a2d0 --- /dev/null +++ b/src/areas/connection.js @@ -0,0 +1,239 @@ +/* + connection.js - ESP3D WebUI area file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +import { h } from "preact" +import { useEffect, useState, useRef } from "preact/hooks" +import { useUiContext, useSettingsContext, useUiContextFn } from "../contexts" +import { Loading } from "../components/Controls" +import { AppLogo } from "../targets" +import { + Minus, + HardDrive, + Frown, + AlertTriangle, + Slash, + Lock, +} from "preact-feather" +import { T } from "../components/Translations" +import { espHttpURL } from "../components/Helpers" +import { restartdelay } from "../targets" + +/* + * Local const + * + */ +const ConnectionContainer = () => { + const { connection } = useUiContext() + const { connectionSettings } = useSettingsContext() + const timerCtrl = useRef() + let contentIcon + let contentSubtitle + let contentTitle + let contentAction + let intervalTimer = 0 + + if ( + !connection.connectionState.connected || + !connection.connectionState.authenticate || + connection.connectionState.updating + ) { + const refreshTimer = () => { + if (intervalTimer > 0) { + intervalTimer-- + if (timerCtrl) timerCtrl.current.innerHTML = intervalTimer + setTimeout(refreshTimer, 1000) + } + } + const onclick = (e) => { + useUiContextFn.haptic() + connection.setConnectionState({ + connected: false, + authenticate: connection.connectionState.authenticate, + page: "connecting", + }) + window.location.href = espHttpURL() + } + switch (connection.connectionState.page) { + case "notauthenticated": + useUiContextFn.beepError() + contentTitle = T("S1") //"Connection error" + contentIcon = + contentSubtitle = T("S145") //"Authentication required" + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S22") + + ")" + contentAction = ( + + ) + break + //No connection + case "error": + useUiContextFn.beepError() + contentTitle = T("S1") //"Connection error" + contentIcon = + contentSubtitle = T("S5") //"Cannot connect with board" + if (connection.connectionState.extraMsg) + contentSubtitle += + ": " + connection.connectionState.extraMsg + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S22") + + ")" + contentAction = ( + + ) + break + //Session timeout + case "sessiontimeout": + //Error connection lost + case "connectionlost": + useUiContextFn.beepError() + contentTitle = T("S1") //"Connection error" + contentIcon = + contentSubtitle = + connection.connectionState.page == "connectionlost" + ? T("S10") + : T("S173") //"Connection with board is lost" + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S9") + + ")" + contentAction = ( + + ) + break + //Disconnected + case "already connected": + useUiContextFn.beep() + contentTitle = T("S9") + contentIcon = + contentSubtitle = T("S3") + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S9") + + ")" + contentAction = ( + + ) + break + //restart + case "restart": + intervalTimer = restartdelay + setTimeout(refreshTimer, 1000) + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S35") + + ")" + contentTitle = T("S35") //"restarting"; + contentIcon = ( +
+ +
+ ) + contentSubtitle = ( + + {T("S60")} + {restartdelay} + {T("S119")} + + ) + contentAction = "" + break + default: //"Please wait..." + if (connection.connectionState.updating) { + contentTitle = T("S35") //"restarting"; + connection.setConnectionState({ + connected: connection.connectionState.connected, + authenticate: connection.connectionState.authenticate, + page: connection.connectionState.page, + updating: false, + }) + } else { + document.title = + (connectionSettings.current && + connectionSettings.current.Hostname + ? connectionSettings.current.Hostname + : "ESP3D") + + "(" + + T("S2") + + ")" + contentTitle = T("S2") //"Connecting"; + } + contentIcon = ( +
+ +
+ ) + contentSubtitle = T("S60") + contentAction = "" + } + return ( +
+
+
+
+ + + {contentIcon} + + +
+
+
{contentTitle}
+
{contentSubtitle}
+
{contentAction}
+
+
+ ) + } +} + +export { ConnectionContainer } diff --git a/src/areas/footer.js b/src/areas/footer.js new file mode 100644 index 00000000..c3cd2b9d --- /dev/null +++ b/src/areas/footer.js @@ -0,0 +1,27 @@ +/* + footer.js - ESP3D WebUI footer file + + Copyright (c) 2020 Luc Lebosse. All rights reserved. + + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with This code; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +import { h } from "preact" + +//Nothing for the moment +const FooterContainer = () => { + return