├── .gitignore ├── LICENSE ├── README.md ├── compress_html.py ├── esphome.html ├── firmware ├── .DS_Store ├── esp32dev │ ├── BLLC_13.12.23.bin │ ├── BLLC_16.5.24.bin │ ├── BLLC_18.3.24.bin │ ├── BLLC_19.3.24.bin │ ├── BLLC_2025.1.22.bin │ ├── BLLC_26.3.24.bin │ ├── BLLC_28.1.24.bin │ ├── BLLC_30.12.23.bin │ ├── BLLC_5.1.24.bin │ ├── BLLC_Experimental_04.11.23.bin │ ├── BLLC_Experimental_05.11.23.bin │ ├── BLLC_Experimental_10.1.24.bin │ ├── BLLC_Experimental_11.12.23.1.bin │ ├── BLLC_Experimental_11.12.23.bin │ ├── BLLC_Experimental_12.10.23.bin │ ├── BLLC_Experimental_12.12.23.bin │ ├── BLLC_Experimental_13.12.23.bin │ ├── BLLC_Experimental_14.10.23.bin │ ├── BLLC_Experimental_15.05.25.bin │ ├── BLLC_Experimental_16.12.23.bin │ ├── BLLC_Experimental_16.2.24.bin │ ├── BLLC_Experimental_17.12.23.bin │ ├── BLLC_Experimental_18.10.23.bin │ ├── BLLC_Experimental_19.10.23.bin │ ├── BLLC_Experimental_20.10.23.bin │ ├── BLLC_Experimental_22.2.24.bin │ ├── BLLC_Experimental_24.11.23.bin │ ├── BLLC_Experimental_25.2.24.bin │ ├── BLLC_Experimental_27.1.24.bin │ ├── BLLC_Experimental_29.11.23.bin │ ├── BLLC_Experimental_29.12.23.bin │ ├── BLLC_V01.bin │ ├── BLLC_V02.bin │ ├── BLLC_V03.bin │ ├── BLLC_V04.bin │ ├── BLLC_V05.bin │ ├── BLLC_V06.bin │ └── BLLC_V07.bin ├── esp8266 │ ├── BLLC_13.12.23.bin │ ├── BLLC_30.12.23.bin │ ├── BLLC_5.1.24.bin │ ├── BLLC_Experimental_04.11.23.bin │ ├── BLLC_Experimental_05.11.23.bin │ ├── BLLC_Experimental_10.1.24.bin │ ├── BLLC_Experimental_11.12.23.1.bin │ ├── BLLC_Experimental_11.12.23.bin │ ├── BLLC_Experimental_12.10.23.bin │ ├── BLLC_Experimental_12.12.23.bin │ ├── BLLC_Experimental_13.12.23.bin │ ├── BLLC_Experimental_14.10.23.bin │ ├── BLLC_Experimental_16.12.23.bin │ ├── BLLC_Experimental_17.12.23.bin │ ├── BLLC_Experimental_18.10.23.bin │ ├── BLLC_Experimental_19.10.23.bin │ ├── BLLC_Experimental_20.10.23.bin │ ├── BLLC_Experimental_24.11.23.bin │ ├── BLLC_Experimental_29.11.23.bin │ ├── BLLC_Experimental_29.12.23.bin │ ├── BLLC_V01.bin │ ├── BLLC_V02.bin │ ├── BLLC_V03.bin │ ├── BLLC_V04.bin │ ├── BLLC_V05.bin │ ├── BLLC_V06.bin │ └── BLLC_V07.bin ├── manifest.json └── manifestexpirimental.json ├── include └── README ├── lib ├── .DS_Store └── README ├── merge_firmware.py ├── platformio.ini ├── pre_build.py ├── src ├── blled │ ├── AutoGrowBufferStream.h │ ├── filesystem.h │ ├── leds.h │ ├── logSerial.h │ ├── mqttmanager.h │ ├── mqttparsingutility.h │ ├── serialmanager.h │ ├── ssdp.h │ ├── types.h │ ├── web-server.h │ └── wifi-manager.h ├── main.cpp └── www │ ├── backupRestore.html │ ├── blled.svg │ ├── colorpicker.html.unused │ ├── favicon.png │ ├── particleCanvas.js │ ├── setupPage.html │ ├── setupPageOld.html │ ├── style.css │ ├── updatePage.html │ ├── webSerialPage.html │ └── wifiSetup.html └── test ├── README └── printerjsonexample.json /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode 3 | .firmware 4 | build/ 5 | BLLEDController.code-workspace 6 | .python-version 7 | src/www/www.h 8 | .DS_Store 9 | **/.DS_Store 10 | .github 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BLLEDController and Firmware License - Version 2.0 2 | 3 | This license ("License") governs the use, distribution, and modification of the Bambulanb Controller's code, firmware, forks, and firmware of forks ("Material"). By using, distributing, or modifying the Material, you agree to abide by the terms and conditions of this License. 4 | 5 | You are free to: 6 | 7 | - Share: Copy and redistribute the Material in any medium or format. 8 | - Adapt: Remix, transform, and build upon the Material. 9 | 10 | Under the following terms: 11 | 12 | - Attribution: You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 13 | - Non-commercial: You may not use the Material for commercial purposes. 14 | - Device and DIY Non-Commercial Restriction: The Material may only be used for the "bambulanb controller" device or for non-commercial DIY projects. 15 | - Share-alike: If you remix, transform, or build upon the Material, you must distribute your contributions under the same license as the original. 16 | 17 | No additional restrictions: You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 18 | 19 | For the full text of the CC BY-NC-SA 4.0 license, please visit: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode 20 | 21 | This License is based on the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The terms of that license apply to the extent they do not conflict with the terms of this License. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BL Led Controller 2 | 3 | The BL Led Controller is an ESP8266 / ESP32 based device that connects to your Bambulab X1,X1C,P1P Or P1S and controls the LED strip based on the state of the printer. 4 | 5 | ### Flashing and Setup 6 | 1. go to the [Web Flasher](https://dutchdevelop.github.io/blledsetup/) 7 | 2. connect your ESP32 8 | 3. Select the Firmware build you want 9 | 4. Click on Flash 10 | 5. Search and connect to a WiFi Hotspot called "BLLED-AP" 11 | 6. Surf to http://192.168.4.1 12 | 7. Select your WiFi and Enter passwort, Optional enter all the Printer informations 13 | 8. enjoy :) 14 | 15 | ### Features 16 | WiFi Setup: 17 | ![1000063279](https://github.com/user-attachments/assets/1cbe01fd-ce1f-4664-909e-7f6f50f6d80a) 18 | 19 | OTA Firmware Update: 20 | ![1000063277](https://github.com/user-attachments/assets/853fa8cd-def6-47e9-8b23-4a4289651510) 21 | 22 | - Connects to Bambulab X1,X1C,P1P Or P1S 23 | - Controls LED strip based on printer state 24 | 25 | ### Development Environment 26 | 27 | To contribute to the BL Led Controller project, you'll need the following tools: 28 | 29 | ### Tools & Libraries Used 30 | 31 | - [Visual Studio Code](https://code.visualstudio.com/): A lightweight and powerful source code editor. 32 | - [PlatformIO](https://platformio.org/): An open-source ecosystem for IoT development. 33 | - [Python](https://www.python.org/): A programming language used for scripting and automation. 34 | - [qpdf](https://qpdf.sourceforge.io/): A command-line tool and library of compression tools (`gzip`) 35 | 36 | ### Building and Running the Project 37 | 1. Clone the repository to your local machine. 38 | 2. Open the project folder in Visual Studio Code. 39 | 3. Ensure that PlatformIO is installed and configured in your Visual Studio Code environment. 40 | 4. Connect your BLLED device ESP32 to your computer. 41 | 6. Build the project by clicking on the PlatformIO icon in the sidebar and selecting "Build" from the available options. 42 | 7. Once the build process is complete, upload the firmware to your device using the "Upload" option in PlatformIO. 43 | 8. After uploading the firmware, your BL Led Controller device should be ready to use. 44 | 45 | ### Setup Instructions 46 | Once you have uploaded the firmware to your device, please visit the [dutchdevelop.com/bl-led-controller](https://dutchdevelop.com/bl-led-controller) website for detailed setup instructions. 47 | 48 | 49 | ### Development Notes 50 | 51 | #### Generating .h Files for Compressed HTML 52 | 53 | In embedded applications, HTML content is efficiently stored in PROGMEM memory. To achieve this, .h files are generated from compressed HTML files for webpages (i.e., `src/www/setuppage.html`) that are run on the device. 54 | 55 | - The `compress_html.py` is used to compress HTML files and generate corresponding .h files and is integrated into the build process and executed as a pre-build step in `platform.ini` 56 | - The generated .h files should not be checked into git (see `.gitignore`) 57 | 58 | ### License 59 | 60 | The BL Led Controller is released under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license. See the [LICENSE](https://github.com/DutchDevelop/BLLEDController/blob/main/LICENSE) file for more details. 61 | 62 | ### Credits 63 | - **[DutchDeveloper](https://dutchdevelop.com/)**: Lead programmer 64 | - **[Modbot](https://github.com/Modbot)**: Tester for X1C, P1P & P1S 65 | - **[xps3riments](https://github.com/xps3riments)**: Inspiration for the foundation of the code 66 | - **[longrackslabs](https://github.com/longrackslabs)**: Build process, documentation, developer & community support 67 | 68 | ### Author 69 | 70 | This project was created by [DutchDeveloper](https://dutchdevelop.com/). 71 | -------------------------------------------------------------------------------- /compress_html.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | import os 3 | import glob 4 | 5 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 6 | HTML_DIR = os.path.join(SCRIPT_DIR, "src", "www") 7 | 8 | OUTPUT_HEADER_NAME = "www.h" 9 | OUTPUT_HEADER = os.path.join(HTML_DIR, OUTPUT_HEADER_NAME) 10 | 11 | SUPPORTED_EXTENSIONS = ["*.html", "*.js", "*.css","*.svg","*.png"] 12 | 13 | MIME_TYPES = { 14 | ".html": "text/html", 15 | ".htm": "text/html", 16 | ".js": "application/javascript", 17 | ".css": "text/css", 18 | ".json": "application/json", 19 | ".png": "image/png", 20 | ".jpg": "image/jpeg", 21 | ".jpeg": "image/jpeg", 22 | ".svg": "image/svg+xml", 23 | ".ico": "image/x-icon", 24 | } 25 | 26 | def generate_header_start(f): 27 | f.write("#ifndef WWW_H\n#define WWW_H\n\n") 28 | f.write("#include \n\n") 29 | 30 | def generate_header_end(f): 31 | f.write("\n#endif // WWW_H\n") 32 | 33 | def guess_mime_type(filename): 34 | _, ext = os.path.splitext(filename.lower()) 35 | return MIME_TYPES.get(ext, "application/octet-stream") 36 | 37 | def compress_and_append_file(input_file, f): 38 | compressed_file = input_file + ".gz" 39 | 40 | with open(input_file, "rb") as infile: 41 | with gzip.open(compressed_file, "wb", compresslevel=6) as outfile: 42 | outfile.write(infile.read()) 43 | 44 | with open(compressed_file, "rb") as cf: 45 | data = cf.read() 46 | 47 | array_name = os.path.basename(input_file).replace(".", "_") 48 | 49 | f.write(f"const uint8_t {array_name}_gz[] PROGMEM = {{\n") 50 | for i in range(0, len(data), 16): 51 | line = ', '.join(f'0x{b:02x}' for b in data[i:i+16]) 52 | f.write(f" {line},\n") 53 | f.write("};\n\n") 54 | f.write(f"const unsigned int {array_name}_gz_len = {len(data)};\n") 55 | f.write(f"const char * {array_name}_gz_mime = \"{guess_mime_type(input_file)}\";\n\n") 56 | 57 | os.remove(compressed_file) 58 | print(f"Appended: {array_name}_gz to {OUTPUT_HEADER_NAME} with MIME {guess_mime_type(input_file)}") 59 | 60 | if __name__ == "__main__": 61 | if not os.path.isdir(HTML_DIR): 62 | print(f"Error: {HTML_DIR} is not a valid directory") 63 | exit(1) 64 | 65 | files_to_process = [] 66 | for pattern in SUPPORTED_EXTENSIONS: 67 | files_to_process.extend(glob.glob(os.path.join(HTML_DIR, pattern))) 68 | 69 | if not files_to_process: 70 | print(f"No matching files found in {HTML_DIR}") 71 | exit(0) 72 | 73 | with open(OUTPUT_HEADER, "w") as f: 74 | generate_header_start(f) 75 | 76 | for file in files_to_process: 77 | compress_and_append_file(file, f) 78 | 79 | generate_header_end(f) 80 | 81 | print(f"\n✅ All files combined into: {OUTPUT_HEADER}") 82 | -------------------------------------------------------------------------------- /esphome.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /firmware/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/.DS_Store -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_13.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_13.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_16.5.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_16.5.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_18.3.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_18.3.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_19.3.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_19.3.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_2025.1.22.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_2025.1.22.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_26.3.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_26.3.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_28.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_28.1.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_30.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_30.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_5.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_5.1.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_04.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_04.11.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_05.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_05.11.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_10.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_10.1.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_11.12.23.1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_11.12.23.1.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_11.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_11.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_12.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_12.10.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_12.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_12.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_13.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_13.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_14.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_14.10.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_15.05.25.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_15.05.25.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_16.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_16.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_16.2.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_16.2.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_17.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_17.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_18.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_18.10.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_19.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_19.10.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_20.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_20.10.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_22.2.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_22.2.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_24.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_24.11.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_25.2.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_25.2.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_27.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_27.1.24.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_29.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_29.11.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_Experimental_29.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_Experimental_29.12.23.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V01.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V01.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V02.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V02.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V03.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V03.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V04.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V04.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V05.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V05.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V06.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V06.bin -------------------------------------------------------------------------------- /firmware/esp32dev/BLLC_V07.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp32dev/BLLC_V07.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_13.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_13.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_30.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_30.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_5.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_5.1.24.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_04.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_04.11.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_05.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_05.11.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_10.1.24.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_10.1.24.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_11.12.23.1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_11.12.23.1.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_11.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_11.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_12.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_12.10.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_12.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_12.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_13.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_13.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_14.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_14.10.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_16.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_16.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_17.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_17.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_18.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_18.10.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_19.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_19.10.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_20.10.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_20.10.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_24.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_24.11.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_29.11.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_29.11.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_Experimental_29.12.23.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_Experimental_29.12.23.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V01.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V01.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V02.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V02.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V03.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V03.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V04.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V04.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V05.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V05.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V06.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V06.bin -------------------------------------------------------------------------------- /firmware/esp8266/BLLC_V07.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/firmware/esp8266/BLLC_V07.bin -------------------------------------------------------------------------------- /firmware/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BLLED Firmware", 3 | "version": "Stable BLLC_16.5.24", 4 | "new_install_prompt_erase": true, 5 | "builds": [ 6 | { 7 | "chipFamily": "ESP32", 8 | "parts": [ 9 | { "path": "esp32dev/BLLC_16.5.24.bin", "offset": 0 } 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /firmware/manifestexpirimental.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BLLEDCONTROLLER", 3 | "version": "Experimental 25.2.24", 4 | "new_install_prompt_erase": true, 5 | "builds": [ 6 | { 7 | "chipFamily": "ESP32", 8 | "parts": [ 9 | { "path": "esp32dev/BLLC_Experimental_25.2.24.bin", "offset": 0 } 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/lib/.DS_Store -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /merge_firmware.py: -------------------------------------------------------------------------------- 1 | Import("env") 2 | import os 3 | import shutil 4 | 5 | # === Konfiguration === 6 | ENABLE_MERGE_BIN = True # Steuert ob Merge ausgeführt wird (OTA .bin.ota wird immer erstellt!) 7 | # ====================== 8 | 9 | BUILD_DIR = env.subst("$BUILD_DIR") 10 | PROGNAME = env.subst("${PROGNAME}") 11 | APP_BIN = os.path.normpath(os.path.join(BUILD_DIR, f"{PROGNAME}.bin")) 12 | MERGED_BIN = os.path.normpath(os.path.join(BUILD_DIR, f"{PROGNAME}_merged.bin")) 13 | BOARD_CONFIG = env.BoardConfig() 14 | 15 | project_name = env.GetProjectOption("custom_project_name") or "Firmware" 16 | version = env.GetProjectOption("custom_version") or "0.0.0" 17 | firmware_filename_merged = f"{project_name}_V{version}.bin" 18 | firmware_filename_ota = f"{project_name}_V{version}.bin.ota" 19 | 20 | firmware_path_merged = os.path.normpath(os.path.join(env.subst("$PROJECT_DIR"), ".firmware", firmware_filename_merged)) 21 | firmware_path_ota = os.path.normpath(os.path.join(env.subst("$PROJECT_DIR"), ".firmware", firmware_filename_ota)) 22 | 23 | def copy_bin_as_ota(source, target, env): 24 | os.makedirs(os.path.dirname(firmware_path_ota), exist_ok=True) 25 | shutil.copyfile(APP_BIN, firmware_path_ota) 26 | print(f'Firmware (OTA, uncompressed) copied to: {firmware_path_ota}') 27 | 28 | def merge_bin(source, target, env): 29 | flash_images = env.Flatten(env.get("FLASH_EXTRA_IMAGES", [])) 30 | app_offset = env.subst("$ESP32_APP_OFFSET") or "0x10000" 31 | flash_images += [app_offset, APP_BIN] 32 | 33 | flash_args = [ 34 | os.path.normpath(env.subst(str(x))) if not str(x).startswith("0x") else str(x) 35 | for x in flash_images 36 | ] 37 | 38 | cmd = [ 39 | env.subst("$PYTHONEXE"), 40 | env.subst("$OBJCOPY"), 41 | "--chip", BOARD_CONFIG.get("build.mcu", "esp32"), 42 | "merge_bin", 43 | "--fill-flash-size", BOARD_CONFIG.get("upload.flash_size", "4MB"), 44 | "--output", MERGED_BIN, 45 | ] + flash_args 46 | 47 | command_str = " ".join(f'"{c}"' if " " in c and not c.startswith('"') else c for c in cmd) 48 | print("Running merge_bin:\n ", command_str.replace('"', '\"')) 49 | result = env.Execute(command_str) 50 | 51 | os.makedirs(os.path.dirname(firmware_path_merged), exist_ok=True) 52 | shutil.copyfile(MERGED_BIN, firmware_path_merged) 53 | print(f'Firmware (merged) copied to: {firmware_path_merged}') 54 | return result 55 | 56 | # Immer OTA-Datei erzeugen (unkomprimiert, mit Endung .bin.ota) 57 | env.AddPostAction(APP_BIN, copy_bin_as_ota) 58 | 59 | # Optional merged .bin erzeugen 60 | if ENABLE_MERGE_BIN: 61 | env.AddPostAction(APP_BIN, merge_bin) 62 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32dev] 12 | custom_project_name = BLLEDController 13 | custom_project_codename = Balder 14 | custom_version = 1.0.0.nightly.250525.1 ; for github release name the release VX.Y.Z X=(Incerase when Modifications oder new functions Break the API/webUI) Y=(Incerase for add oder modify changes that not breake the API/webUI until X incearase) Z=(Incerase for bugfix releases until minor is incerase) 15 | platform = espressif32 16 | board = esp32dev 17 | framework = arduino 18 | monitor_speed = 115200 19 | upload_speed = 921600 20 | board_build.filesystem = littlefs 21 | build_flags = 22 | -DVERSION=${this.custom_version} 23 | -DSTRVERSION=\""${this.custom_version}"\" 24 | extra_scripts = 25 | pre:pre_build.py 26 | merge_firmware.py 27 | lib_deps = 28 | bblanchon/ArduinoJson@7.4.1 29 | knolleary/pubsubclient@2.8.0 30 | luc-github/ESP32SSDP@1.2.1 31 | ESP32Async/AsyncTCP 32 | ESP32Async/ESPAsyncWebServer 33 | mathieucarbou/MycilaWebSerial@^8.1.1 -------------------------------------------------------------------------------- /pre_build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | def compress_html_files(): 5 | # Change the working directory to the location of the compression script 6 | os.chdir("src/www") 7 | 8 | # List of HTML files to compress 9 | html_files = ["setuppage.html, updatepage.html"] 10 | for html_file in html_files: 11 | print("Compressing file:", html_file) 12 | subprocess.run(["python", "../../compress_html.py", html_file]) 13 | 14 | compress_html_files() -------------------------------------------------------------------------------- /src/blled/AutoGrowBufferStream.h: -------------------------------------------------------------------------------- 1 | //Code from Xtouchautogrowstream 2 | #ifndef AutoGrowBufferStream_h 3 | #define AutoGrowBufferStream_h 4 | 5 | #include 6 | #include 7 | 8 | #define BUFFER_INCREMENTS 128 9 | 10 | class AutoGrowBufferStream : public Stream 11 | { 12 | private: 13 | uint16_t _len; 14 | uint16_t buffer_size; 15 | char* _buffer; 16 | 17 | public: 18 | AutoGrowBufferStream() { 19 | this->_len = 0; 20 | this->_buffer = (char*)malloc(BUFFER_INCREMENTS); 21 | this->buffer_size = BUFFER_INCREMENTS; 22 | } 23 | 24 | ~AutoGrowBufferStream() { 25 | free(this->_buffer); 26 | } 27 | 28 | virtual size_t write(uint8_t byte) { 29 | if (this->_len + 1 > this->buffer_size) { 30 | auto tmp = (char*)realloc(this->_buffer, this->buffer_size + BUFFER_INCREMENTS); 31 | if (tmp == NULL) { 32 | LogSerial.println(F("Failed to grow buffer")); 33 | return 0; 34 | } 35 | this->_buffer = tmp; 36 | this->buffer_size += BUFFER_INCREMENTS; 37 | } 38 | this->_buffer[this->_len] = byte; 39 | this->_len++; 40 | return 1; 41 | } 42 | 43 | virtual int read() { 44 | return 0; 45 | } 46 | 47 | virtual int available() { 48 | return 1; 49 | } 50 | 51 | virtual void flush() { 52 | this->_len = 0; 53 | this->_buffer = (char*)realloc(this->_buffer, BUFFER_INCREMENTS); 54 | this->buffer_size = BUFFER_INCREMENTS; 55 | } 56 | 57 | int peek() { 58 | return 0; 59 | } 60 | 61 | const uint16_t current_length() const { return _len; } 62 | const char* get_buffer() const { return _buffer; } 63 | const char* get_string() const { 64 | _buffer[_len] = '\0'; 65 | return _buffer; 66 | } 67 | 68 | using Print::write; 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/blled/filesystem.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLLEDFILESYSTEM 2 | #define _BLLEDFILESYSTEM 3 | 4 | #include 5 | #include "FS.h" 6 | 7 | #include 8 | #include 9 | #include "types.h" 10 | 11 | const char *configPath = "/blledconfig.json"; 12 | 13 | char *generateRandomString(int length) 14 | { 15 | static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 16 | int charsetLength = strlen(charset); 17 | 18 | char *randomString = new char[length + 1]; 19 | for (int i = 0; i < length; i++) 20 | { 21 | int randomIndex = random(0, charsetLength); 22 | randomString[i] = charset[randomIndex]; 23 | } 24 | randomString[length] = '\0'; 25 | 26 | return randomString; 27 | } 28 | 29 | void saveFileSystem() 30 | { 31 | LogSerial.println(F("Saving config")); 32 | 33 | JsonDocument json; 34 | json["ssid"] = globalVariables.SSID; 35 | json["appw"] = globalVariables.APPW; 36 | json["HTTPUser"] = securityVariables.HTTPUser; 37 | json["HTTPPass"] = securityVariables.HTTPPass; 38 | json["printerIp"] = printerConfig.printerIP; 39 | json["accessCode"] = printerConfig.accessCode; 40 | json["serialNumber"] = printerConfig.serialNumber; 41 | // json["webpagePassword"] = printerConfig.webpagePassword; 42 | json["bssi"] = printerConfig.BSSID; 43 | json["brightness"] = printerConfig.brightness; 44 | // LED Behaviour (Choose One) 45 | json["maintMode"] = printerConfig.maintMode; 46 | json["discoMode"] = printerConfig.discoMode; 47 | json["replicatestate"] = printerConfig.replicatestate; 48 | // Running Color 49 | json["runningRGB"] = printerConfig.runningColor.RGBhex; 50 | json["runningWW"] = printerConfig.runningColor.ww; 51 | json["runningCW"] = printerConfig.runningColor.cw; 52 | // Test LED Colors 53 | json["showtestcolor"] = printerConfig.testcolorEnabled; 54 | json["testRGB"] = printerConfig.testColor.RGBhex; 55 | json["testWW"] = printerConfig.testColor.ww; 56 | json["testCW"] = printerConfig.testColor.cw; 57 | json["debugwifi"] = printerConfig.debugwifi; 58 | // Options 59 | json["finishindication"] = printerConfig.finishindication; 60 | json["finishColor"] = printerConfig.finishColor.RGBhex; 61 | json["finishWW"] = printerConfig.finishColor.ww; 62 | json["finishCW"] = printerConfig.finishColor.cw; 63 | json["finishExit"] = printerConfig.finishExit; 64 | json["finish_check"] = printerConfig.finish_check; 65 | json["finishTimerMins"] = printerConfig.finishTimeOut; 66 | json["inactivityEnabled"] = printerConfig.inactivityEnabled; 67 | json["inactivityTimeOut"] = printerConfig.inactivityTimeOut; 68 | // Debugging 69 | json["debuging"] = printerConfig.debuging; 70 | json["debugingchange"] = printerConfig.debugingchange; 71 | json["mqttdebug"] = printerConfig.mqttdebug; 72 | // Printer Dependant 73 | json["p1Printer"] = printerVariables.isP1Printer; 74 | json["doorSwitch"] = printerVariables.useDoorSwtich; 75 | // Customise LED Colors (during Lidar) 76 | json["stage14RGB"] = printerConfig.stage14Color.RGBhex; 77 | json["stage14WW"] = printerConfig.stage14Color.ww; 78 | json["stage14CW"] = printerConfig.stage14Color.cw; 79 | json["stage1RGB"] = printerConfig.stage1Color.RGBhex; 80 | json["stage1WW"] = printerConfig.stage1Color.ww; 81 | json["stage1CW"] = printerConfig.stage1Color.cw; 82 | json["stage8RGB"] = printerConfig.stage8Color.RGBhex; 83 | json["stage8WW"] = printerConfig.stage8Color.ww; 84 | json["stage8CW"] = printerConfig.stage8Color.cw; 85 | json["stage9RGB"] = printerConfig.stage9Color.RGBhex; 86 | json["stage9WW"] = printerConfig.stage9Color.ww; 87 | json["stage9CW"] = printerConfig.stage9Color.cw; 88 | json["stage10RGB"] = printerConfig.stage10Color.RGBhex; 89 | json["stage10WW"] = printerConfig.stage10Color.ww; 90 | json["stage10CW"] = printerConfig.stage10Color.cw; 91 | // Customise LED Colors 92 | json["errordetection"] = printerConfig.errordetection; 93 | json["wifiRGB"] = printerConfig.wifiRGB.RGBhex; 94 | json["wifiWW"] = printerConfig.wifiRGB.ww; 95 | json["wifiCW"] = printerConfig.wifiRGB.cw; 96 | json["pauseRGB"] = printerConfig.pauseRGB.RGBhex; 97 | json["pauseWW"] = printerConfig.pauseRGB.ww; 98 | json["pauseCW"] = printerConfig.pauseRGB.cw; 99 | json["firstlayerRGB"] = printerConfig.firstlayerRGB.RGBhex; 100 | json["firstlayerWW"] = printerConfig.firstlayerRGB.ww; 101 | json["firstlayerCW"] = printerConfig.firstlayerRGB.cw; 102 | json["nozzleclogRGB"] = printerConfig.nozzleclogRGB.RGBhex; 103 | json["nozzleclogWW"] = printerConfig.nozzleclogRGB.ww; 104 | json["nozzleclogCW"] = printerConfig.nozzleclogRGB.cw; 105 | json["hmsSeriousRGB"] = printerConfig.hmsSeriousRGB.RGBhex; 106 | json["hmsSeriousWW"] = printerConfig.hmsSeriousRGB.ww; 107 | json["hmsSeriousCW"] = printerConfig.hmsSeriousRGB.cw; 108 | json["hmsFatalRGB"] = printerConfig.hmsFatalRGB.RGBhex; 109 | json["hmsFatalWW"] = printerConfig.hmsFatalRGB.ww; 110 | json["hmsFatalCW"] = printerConfig.hmsFatalRGB.cw; 111 | json["filamentRunoutRGB"] = printerConfig.filamentRunoutRGB.RGBhex; 112 | json["filamentRunoutWW"] = printerConfig.filamentRunoutRGB.ww; 113 | json["filamentRunoutCW"] = printerConfig.filamentRunoutRGB.cw; 114 | json["frontCoverRGB"] = printerConfig.frontCoverRGB.RGBhex; 115 | json["frontCoverWW"] = printerConfig.frontCoverRGB.ww; 116 | json["frontCoverCW"] = printerConfig.frontCoverRGB.cw; 117 | json["nozzleTempRGB"] = printerConfig.nozzleTempRGB.RGBhex; 118 | json["nozzleTempWW"] = printerConfig.nozzleTempRGB.ww; 119 | json["nozzleTempCW"] = printerConfig.nozzleTempRGB.cw; 120 | json["bedTempRGB"] = printerConfig.bedTempRGB.RGBhex; 121 | json["bedTempWW"] = printerConfig.bedTempRGB.ww; 122 | json["bedTempCW"] = printerConfig.bedTempRGB.cw; 123 | 124 | File configFile = LittleFS.open(configPath, "w"); 125 | if (!configFile) 126 | { 127 | LogSerial.println(F("Failed to save config")); 128 | return; 129 | } 130 | serializeJson(json, configFile); 131 | configFile.close(); 132 | LogSerial.println(F("Config Saved")); 133 | } 134 | 135 | void loadFileSystem() 136 | { 137 | LogSerial.println(F("Loading config")); 138 | 139 | File configFile; 140 | int attempts = 0; 141 | while (attempts < 2) 142 | { 143 | configFile = LittleFS.open(configPath, "r"); 144 | if (configFile) 145 | { 146 | break; 147 | } 148 | attempts++; 149 | LogSerial.println(F("Failed to open config file, retrying..")); 150 | delay(2000); 151 | } 152 | if (!configFile) 153 | { 154 | LogSerial.print(F("Failed to open config file after ")); 155 | LogSerial.print(attempts); 156 | LogSerial.println(F(" retries")); 157 | 158 | LogSerial.println(F("Clearing config")); 159 | // LittleFS.remove(configPath); 160 | saveFileSystem(); 161 | return; 162 | } 163 | size_t size = configFile.size(); 164 | std::unique_ptr buf(new char[size]); 165 | configFile.readBytes(buf.get(), size); 166 | 167 | JsonDocument json; 168 | auto deserializeError = deserializeJson(json, buf.get()); 169 | 170 | if (!deserializeError) 171 | { 172 | strcpy(globalVariables.SSID, json["ssid"]); 173 | strcpy(globalVariables.APPW, json["appw"]); 174 | strlcpy(securityVariables.HTTPUser, json["HTTPUser"] | "", sizeof(securityVariables.HTTPUser)); 175 | strlcpy(securityVariables.HTTPPass, json["HTTPPass"] | "", sizeof(securityVariables.HTTPPass)); 176 | strcpy(printerConfig.printerIP, json["printerIp"]); 177 | strcpy(printerConfig.accessCode, json["accessCode"]); 178 | strcpy(printerConfig.serialNumber, json["serialNumber"]); 179 | // strcpy(printerConfig.webpagePassword, json["webpagePassword"]); 180 | strcpy(printerConfig.BSSID, json["bssi"]); 181 | printerConfig.brightness = json["brightness"]; 182 | // LED Behaviour (Choose One) 183 | printerConfig.maintMode = json["maintMode"]; 184 | printerConfig.discoMode = json["discoMode"]; 185 | printerConfig.replicatestate = json["replicatestate"]; 186 | // Running Color 187 | printerConfig.runningColor = hex2rgb(json["runningRGB"], json["runningWW"], json["runningCW"]); 188 | // Test LED Colors 189 | printerConfig.testcolorEnabled = json["showtestcolor"]; 190 | printerConfig.testColor = hex2rgb(json["testRGB"], json["testWW"], json["testCW"]); 191 | printerConfig.debugwifi = json["debugwifi"]; 192 | // Options 193 | printerConfig.finishindication = json["finishindication"]; 194 | printerConfig.finishColor = hex2rgb(json["finishColor"], json["finishWW"], json["finishCW"]); 195 | printerConfig.finishExit = json["finishExit"]; 196 | printerConfig.finishTimeOut = json["finishTimerMins"]; 197 | printerConfig.finish_check = json["finish_check"]; 198 | printerConfig.inactivityEnabled = json["inactivityEnabled"]; 199 | printerConfig.inactivityTimeOut = json["inactivityTimeOut"]; 200 | // Debugging 201 | printerConfig.debuging = json["debuging"]; 202 | printerConfig.debugingchange = json["debugingchange"]; 203 | printerConfig.mqttdebug = json["mqttdebug"]; 204 | // Printer Dependant 205 | printerVariables.isP1Printer = json["p1Printer"]; 206 | printerVariables.useDoorSwtich = json["doorSwitch"]; 207 | printerConfig.stage14Color = hex2rgb(json["stage14RGB"], json["stage14WW"], json["stage14CW"]); 208 | printerConfig.stage1Color = hex2rgb(json["stage1RGB"], json["stage1WW"], json["stage1CW"]); 209 | printerConfig.stage8Color = hex2rgb(json["stage8RGB"], json["stage8WW"], json["stage8CW"]); 210 | printerConfig.stage9Color = hex2rgb(json["stage9RGB"], json["stage9WW"], json["stage9CW"]); 211 | printerConfig.stage10Color = hex2rgb(json["stage10RGB"], json["stage10WW"], json["stage10CW"]); 212 | // Customise LED Colors 213 | printerConfig.errordetection = json["errordetection"]; 214 | printerConfig.wifiRGB = hex2rgb(json["wifiRGB"], json["wifiWW"], json["wifiCW"]); 215 | 216 | printerConfig.pauseRGB = hex2rgb(json["pauseRGB"], json["pauseWW"], json["pauseCW"]); 217 | printerConfig.firstlayerRGB = hex2rgb(json["firstlayerRGB"], json["firstlayerWW"], json["firstlayerCW"]); 218 | printerConfig.nozzleclogRGB = hex2rgb(json["nozzleclogRGB"], json["nozzleclogWW"], json["nozzleclogCW"]); 219 | printerConfig.hmsSeriousRGB = hex2rgb(json["hmsSeriousRGB"], json["hmsSeriousWW"], json["hmsSeriousCW"]); 220 | printerConfig.hmsFatalRGB = hex2rgb(json["hmsFatalRGB"], json["hmsFatalWW"], json["hmsFatalCW"]); 221 | printerConfig.filamentRunoutRGB = hex2rgb(json["filamentRunoutRGB"], json["filamentRunoutWW"], json["filamentRunoutCW"]); 222 | printerConfig.frontCoverRGB = hex2rgb(json["frontCoverRGB"], json["frontCoverWW"], json["frontCoverCW"]); 223 | printerConfig.nozzleTempRGB = hex2rgb(json["nozzleTempRGB"], json["nozzleTempWW"], json["nozzleTempCW"]); 224 | printerConfig.bedTempRGB = hex2rgb(json["bedTempRGB"], json["bedTempWW"], json["bedTempCW"]); 225 | LogSerial.println(F("Loaded config")); 226 | } 227 | else 228 | { 229 | LogSerial.println(F("Failed loading config")); 230 | LogSerial.println(F("Clearing config")); 231 | LittleFS.remove(configPath); 232 | 233 | // LogSerial.println(F("Generating new password")); 234 | // char* pw = generateRandomString(8); 235 | // strcpy(printerConfig.webpagePassword, pw); 236 | } 237 | 238 | configFile.close(); 239 | } 240 | 241 | void deleteFileSystem() 242 | { 243 | LogSerial.println(F("Deleting LittleFS")); 244 | LittleFS.remove(configPath); 245 | } 246 | 247 | bool hasFileSystem() 248 | { 249 | return LittleFS.exists(configPath); 250 | } 251 | 252 | void setupFileSystem() 253 | { 254 | LogSerial.println(F("Mounting LittleFS")); 255 | if (!LittleFS.begin()) 256 | { 257 | LogSerial.println(F("Failed to mount LittleFS")); 258 | LittleFS.format(); 259 | LogSerial.println(F("Formatting LittleFS")); 260 | LogSerial.println(F("Restarting Device")); 261 | delay(1000); 262 | ESP.restart(); 263 | } 264 | LogSerial.println(F("Mounted LittleFS")); 265 | }; 266 | 267 | #endif -------------------------------------------------------------------------------- /src/blled/logSerial.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGSERIAL_H 2 | #define LOGSERIAL_H 3 | 4 | #include 5 | #include 6 | 7 | class AsyncWebServer; 8 | 9 | class LogSerialClass : public Stream { 10 | private: 11 | WebSerial webSerial; 12 | public: 13 | void begin(unsigned long baud = 115200) { 14 | Serial.begin(baud); 15 | } 16 | 17 | void begin(AsyncWebServer* server, unsigned long baud = 115200, size_t bufferSize = 100) { 18 | Serial.begin(baud); 19 | webSerial.begin(server); 20 | webSerial.setBuffer(bufferSize); 21 | webSerial.onMessage([](const std::string& msg) { 22 | Serial.print("[WebSerial RX] "); 23 | Serial.println(msg.c_str()); 24 | }); 25 | } 26 | 27 | void onMessage(std::function cb) { 28 | webSerial.onMessage(cb); 29 | } 30 | 31 | void setBuffer(size_t size) { 32 | webSerial.setBuffer(size); 33 | } 34 | 35 | int available() override { return Serial.available(); } 36 | int read() override { return Serial.read(); } 37 | int peek() override { return Serial.peek(); } 38 | void flush() override { Serial.flush(); } 39 | 40 | size_t write(uint8_t b) override { 41 | Serial.write(b); 42 | webSerial.write(&b, 1); 43 | return 1; 44 | } 45 | 46 | size_t write(const uint8_t* buffer, size_t size) override { 47 | Serial.write(buffer, size); 48 | webSerial.write(buffer, size); 49 | return size; 50 | } 51 | 52 | using Print::write; 53 | operator bool() { return (bool)Serial; } 54 | 55 | 56 | }; 57 | 58 | LogSerialClass LogSerial; 59 | 60 | #endif // LOGSERIAL_H 61 | -------------------------------------------------------------------------------- /src/blled/mqttmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef _MQTTMANAGER 2 | #define _MQTTMANAGER 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "mqttparsingutility.h" 11 | #include "AutoGrowBufferStream.h" 12 | #include "types.h" 13 | #include "leds.h" 14 | 15 | WiFiClientSecure wifiSecureClient; 16 | PubSubClient mqttClient(wifiSecureClient); 17 | 18 | String device_topic; 19 | String report_topic; 20 | String clientId = "BLLED-"; 21 | 22 | AutoGrowBufferStream stream; 23 | 24 | unsigned long mqttattempt = (millis() - 3000); 25 | unsigned long lastMQTTupdate = millis(); 26 | 27 | TaskHandle_t mqttTaskHandle = NULL; 28 | bool mqttTaskRunning = false; 29 | 30 | // With a Default BLLED 31 | // Expected information when viewing MQTT status messages 32 | 33 | // gcode_state stg_cur BLLED LED control Comments 34 | //------------------------------------------------------------------------ 35 | // IDLE -1 White Printer just powered on 36 | // RUNNING -1 White Printer sent print file 37 | // RUNNING 2 White PREHEATING BED 38 | // RUNNING 14 OFF (for Lidar) CLEANING NOZZLE 39 | // RUNNING 1 OFF (for Lidar) BED LEVELING 40 | // RUNNING 8 OFF (for Lidar) CALIBRATING EXTRUSION 41 | // RUNNING 0 White All the printing happens here 42 | // FINISH -1 Green After bed is lowered and filament retracted 43 | // FINISH -1 Green BLLED logic waits for a door interaction 44 | // FINISH -1 White After door interaction 45 | // FINISH -1 OFF Inactivity after 30mins 46 | 47 | void connectMqtt() 48 | { 49 | if (WiFi.status() != WL_CONNECTED || WiFi.getMode() != WIFI_MODE_STA) 50 | { 51 | // Abort MQTT connection attempt when no Wifi 52 | return; 53 | } 54 | if (strlen(printerConfig.printerIP) == 0 || strlen(printerConfig.accessCode) == 0) 55 | { 56 | LogSerial.println(F("[MQTT] Abort connect: printerIP oder accessCode wrong or empty")); 57 | return; 58 | } 59 | if (!mqttClient.connected() && (millis() - mqttattempt) >= 3000) 60 | { 61 | tweenToColor(10, 10, 10, 10, 10); 62 | LogSerial.println(F("Connecting to mqtt...")); 63 | if (mqttClient.connect(clientId.c_str(), "bblp", printerConfig.accessCode)) 64 | { 65 | LogSerial.print(F("MQTT connected, subscribing to MQTT Topic: ")); 66 | LogSerial.println(report_topic); 67 | mqttClient.subscribe(report_topic.c_str()); 68 | printerVariables.online = true; 69 | printerVariables.disconnectMQTTms = 0; 70 | // LogSerial.println(F("Updating LEDs from MQTT connect")); 71 | // updateleds(); 72 | } 73 | else 74 | { 75 | LogSerial.println(F("Failed to connect with error code: ")); 76 | LogSerial.print(mqttClient.state()); 77 | LogSerial.print(F(" ")); 78 | ParseMQTTState(mqttClient.state()); 79 | if (mqttClient.state() == 5) 80 | { 81 | //delay(500); 82 | tweenToColor(127, 0, 0, 0, 0); // light red, indicating not authorized 83 | //delay(500); 84 | mqttattempt = (millis() - 3000); 85 | // LogSerial.println(F("Restarting Device")); 86 | // delay(1000); 87 | // ESP.restart(); 88 | } 89 | } 90 | } 91 | } 92 | 93 | void mqttTask(void *parameter) 94 | { 95 | mqttTaskRunning = true; 96 | 97 | while (true) 98 | { 99 | if (WiFi.status() != WL_CONNECTED || WiFi.getMode() != WIFI_MODE_STA) 100 | { 101 | printerVariables.online = false; 102 | vTaskDelay(pdMS_TO_TICKS(2000)); 103 | continue; 104 | } 105 | 106 | if (!mqttClient.connected()) 107 | { 108 | printerVariables.online = false; 109 | 110 | if (printerVariables.disconnectMQTTms == 0) 111 | { 112 | printerVariables.disconnectMQTTms = millis(); 113 | LogSerial.println(F("[MQTT Task] Disconnected")); 114 | ParseMQTTState(mqttClient.state()); 115 | } 116 | 117 | connectMqtt(); 118 | vTaskDelay(pdMS_TO_TICKS(32)); 119 | } 120 | else 121 | { 122 | printerVariables.disconnectMQTTms = 0; 123 | mqttClient.loop(); 124 | } 125 | 126 | vTaskDelay(pdMS_TO_TICKS(10)); 127 | } 128 | 129 | mqttTaskRunning = false; 130 | vTaskDelete(NULL); 131 | } 132 | 133 | void ParseCallback(char *topic, byte *payload, unsigned int length) 134 | { 135 | JsonDocument messageobject; 136 | JsonDocument filter; 137 | // Rather than showing the entire message to Serial - grabbing only the pertinent bits for BLLED. 138 | // Device Status 139 | filter["print"]["command"] = true; 140 | filter["print"]["fail_reason"] = true; 141 | filter["print"]["gcode_state"] = true; 142 | filter["print"]["print_gcode_action"] = true; 143 | filter["print"]["print_real_action"] = true; 144 | filter["print"]["hms"] = true; 145 | filter["print"]["home_flag"] = true; 146 | filter["print"]["lights_report"] = true; 147 | filter["print"]["stg_cur"] = true; 148 | filter["print"]["print_error"] = true; 149 | filter["print"]["wifi_signal"] = true; 150 | filter["system"]["command"] = true; 151 | filter["system"]["led_mode"] = true; 152 | 153 | auto deserializeError = deserializeJson(messageobject, payload, length, DeserializationOption::Filter(filter)); 154 | if (!deserializeError) 155 | { 156 | if (printerConfig.debuging) 157 | { 158 | LogSerial.println(F("Mqtt message received.")); 159 | LogSerial.print(F("FreeHeap: ")); 160 | LogSerial.println(ESP.getFreeHeap()); 161 | } 162 | 163 | bool Changed = false; 164 | 165 | if (!messageobject["print"]["command"].isNull()) 166 | { 167 | if (messageobject["print"]["command"] == "gcode_line" // gcode_line used a lot during print initialisations - Skip these 168 | || messageobject["print"]["command"] == "project_prepare" // 1 message per print 169 | || messageobject["print"]["command"] == "project_file" // 1 message per print 170 | || messageobject["print"]["command"] == "clean_print_error" // During error (no info) 171 | || messageobject["print"]["command"] == "resume" // After error or pause 172 | || messageobject["print"]["command"] == "get_accessories" // After error or pause 173 | || messageobject["print"]["command"] == "prepare" 174 | || messageobject["print"]["command"] == "extrusion_cali_get") 175 | { // 1 message per print 176 | return; 177 | } 178 | } 179 | if (messageobject.size() == 0) 180 | { 181 | // Null or Filtered message that are not 'Print' or 'System' payload - Ignore these 182 | return; 183 | } 184 | 185 | // Output Filtered MQTT message 186 | if (printerConfig.mqttdebug) 187 | { 188 | LogSerial.print(F("(Filtered) MQTT payload, [")); 189 | LogSerial.print(millis()); 190 | LogSerial.print(F("], ")); 191 | serializeJson(messageobject, LogSerial); 192 | LogSerial.println(); 193 | } 194 | 195 | if (printerConfig.mqttdebug && (printerConfig.maintMode || printerConfig.testcolorEnabled || printerConfig.discoMode || printerConfig.debugwifi)) 196 | { 197 | LogSerial.print(F("MQTT Message Ignored while in ")); 198 | if (printerConfig.maintMode) 199 | LogSerial.print(F("Maintenance")); 200 | if (printerConfig.testcolorEnabled) 201 | LogSerial.print(F("Test Color")); 202 | if (printerConfig.discoMode) 203 | LogSerial.print(F("RGB Cycle")); 204 | if (printerConfig.debugwifi) 205 | LogSerial.print(F("Wifi Debug")); 206 | LogSerial.println(F(" mode")); 207 | return; 208 | } 209 | 210 | // Check for Door Status 211 | if (!messageobject["print"]["home_flag"].isNull()) 212 | { 213 | // https://github.com/greghesp/ha-bambulab/blob/main/custom_components/bambu_lab/pybambu/const.py#L324 214 | 215 | bool doorState = false; 216 | long homeFlag = 0; 217 | homeFlag = messageobject["print"]["home_flag"]; 218 | doorState = bitRead(homeFlag, 23); 219 | 220 | if (printerVariables.doorOpen != doorState) 221 | { 222 | printerVariables.doorOpen = doorState; 223 | 224 | if (printerConfig.debugingchange) 225 | LogSerial.print(F("MQTT Door ")); 226 | if (printerVariables.doorOpen) 227 | { 228 | printerVariables.lastdoorOpenms = millis(); 229 | if (printerConfig.debugingchange) 230 | LogSerial.println(F("Opened")); 231 | } 232 | else 233 | { 234 | if ((millis() - printerVariables.lastdoorClosems) < 6000) 235 | { 236 | printerVariables.doorSwitchTriggered = true; 237 | } 238 | printerVariables.lastdoorClosems = millis(); 239 | if (printerConfig.debugingchange) 240 | LogSerial.println(F("Closed")); 241 | } 242 | Changed = true; 243 | } 244 | } 245 | 246 | // Check BBLP Stage 247 | if (!messageobject["print"]["stg_cur"].isNull()) 248 | { 249 | if (printerVariables.stage != messageobject["print"]["stg_cur"].as()) 250 | { 251 | printerVariables.stage = messageobject["print"]["stg_cur"]; 252 | 253 | if (printerConfig.debugingchange || printerConfig.debuging) 254 | { 255 | LogSerial.print(F("MQTT update - stg_cur now: ")); 256 | LogSerial.println(printerVariables.stage); 257 | } 258 | Changed = true; 259 | } 260 | } 261 | 262 | // Check BBLP GCode State 263 | if (!messageobject["print"]["gcode_state"].isNull() && ((millis() - lastMQTTupdate) > 3000)) 264 | { 265 | String mqttgcodeState = messageobject["print"]["gcode_state"].as(); 266 | 267 | if (mqttgcodeState == "RUNNING" || mqttgcodeState == "PAUSE") 268 | { 269 | // Never turn off light (due to idle timer) while in this state 270 | printerConfig.inactivityStartms = millis(); 271 | } 272 | 273 | // Onchange of gcodeState... 274 | if (printerVariables.gcodeState != mqttgcodeState) 275 | { 276 | if (mqttgcodeState == "FINISH") 277 | { 278 | printerVariables.finished = true; 279 | printerVariables.waitingForDoor = true; 280 | printerConfig.finishStartms = millis(); 281 | printerConfig.finish_check = true; 282 | } 283 | printerVariables.gcodeState = mqttgcodeState; 284 | 285 | if (printerConfig.debugingchange || printerConfig.debuging) 286 | { 287 | LogSerial.print(F("MQTT update - gcode_state now: ")); 288 | LogSerial.println(printerVariables.gcodeState); 289 | } 290 | Changed = true; 291 | } 292 | } 293 | 294 | // Pause Command - quicker, but Only for user generated pause - error & code pauses don't trigger this. 295 | if (!messageobject["print"]["command"].isNull()) 296 | { 297 | if (messageobject["print"]["command"] == "pause") 298 | { 299 | lastMQTTupdate = millis(); 300 | LogSerial.println(F("MQTT update - manual PAUSE")); 301 | printerVariables.gcodeState = "PAUSE"; 302 | Changed = true; 303 | } 304 | } 305 | 306 | // Added a delay so the slower MQTT status message doesn't reverse the "system" commands 307 | if (!messageobject["print"]["lights_report"].isNull() && ((millis() - lastMQTTupdate) > 3000)) 308 | { 309 | JsonArray lightsReport = messageobject["print"]["lights_report"]; 310 | for (JsonObject light : lightsReport) 311 | { 312 | if (light["node"] == "chamber_light") 313 | { 314 | 315 | if (printerVariables.printerledstate != (light["mode"] == "on")) 316 | { 317 | printerVariables.printerledstate = (light["mode"] == "on"); 318 | printerConfig.replicate_update = true; 319 | if (printerConfig.debugingchange || printerConfig.debuging) 320 | { 321 | LogSerial.print(F("MQTT chamber_light now: ")); 322 | LogSerial.println(printerVariables.printerledstate); 323 | } 324 | if (printerVariables.waitingForDoor && printerConfig.finish_check) 325 | { 326 | printerVariables.finished = true; 327 | } 328 | Changed = true; 329 | } 330 | } 331 | } 332 | } 333 | // System Commands are sent quicker than the push_status 334 | // Message only sent onChange 335 | if (!messageobject["system"]["command"].isNull()) 336 | { 337 | if (messageobject["system"]["command"] == "ledctrl") 338 | { 339 | // Ignore Printer sending attempts to turn light on when already on. 340 | if (printerVariables.printerledstate != (messageobject["system"]["led_mode"] == "on")) 341 | { 342 | printerVariables.printerledstate = (messageobject["system"]["led_mode"] == "on"); 343 | printerConfig.replicate_update = true; 344 | lastMQTTupdate = millis(); 345 | if (printerConfig.debugingchange || printerConfig.debuging) 346 | { 347 | LogSerial.print(F("MQTT led_mode now: ")); 348 | LogSerial.println(printerVariables.printerledstate); 349 | } 350 | if (printerVariables.waitingForDoor && printerConfig.finish_check) 351 | { 352 | printerVariables.finished = true; 353 | } 354 | 355 | Changed = true; 356 | } 357 | } 358 | } 359 | 360 | // Bambu Health Management System (HMS) 361 | if (!messageobject["print"]["hms"].isNull()) 362 | { 363 | String oldHMSlevel = ""; 364 | oldHMSlevel = printerVariables.parsedHMSlevel; 365 | 366 | printerVariables.hmsstate = false; 367 | printerVariables.parsedHMSlevel = ""; 368 | for (const auto &hms : messageobject["print"]["hms"].as()) 369 | { 370 | if (ParseHMSSeverity(hms["code"]) != "") 371 | { 372 | printerVariables.hmsstate = true; 373 | printerVariables.parsedHMSlevel = ParseHMSSeverity(hms["code"]); 374 | printerVariables.parsedHMScode = ((uint64_t)hms["attr"] << 32) + (uint64_t)hms["code"]; 375 | } 376 | } 377 | if (oldHMSlevel != printerVariables.parsedHMSlevel) 378 | { 379 | 380 | if (printerVariables.parsedHMScode == 0x0C0003000003000B) 381 | printerVariables.overridestage = 10; 382 | if (printerVariables.parsedHMScode == 0x0300120000020001) 383 | printerVariables.overridestage = 17; 384 | if (printerVariables.parsedHMScode == 0x0700200000030001) 385 | printerVariables.overridestage = 6; 386 | if (printerVariables.parsedHMScode == 0x0300020000010001) 387 | printerVariables.overridestage = 20; 388 | if (printerVariables.parsedHMScode == 0x0300010000010007) 389 | printerVariables.overridestage = 21; 390 | 391 | if (printerConfig.debuging || printerConfig.debugingchange) 392 | { 393 | LogSerial.print(F("MQTT update - parsedHMSlevel now: ")); 394 | if (printerVariables.parsedHMSlevel.length() > 0) 395 | { 396 | LogSerial.print(printerVariables.parsedHMSlevel); 397 | LogSerial.print(F(" Error Code: ")); 398 | // LogSerial.println(F("https://wiki.bambulab.com/en/x1/troubleshooting/how-to-enter-the-specific-code-page")); 399 | int chunk1 = (printerVariables.parsedHMScode >> 48); 400 | int chunk2 = (printerVariables.parsedHMScode >> 32) & 0xFFFF; 401 | int chunk3 = (printerVariables.parsedHMScode >> 16) & 0xFFFF; 402 | int chunk4 = printerVariables.parsedHMScode & 0xFFFF; 403 | char strHMScode[20]; 404 | sprintf(strHMScode, "%04X_%04X_%04X_%04X", chunk1, chunk2, chunk3, chunk4); 405 | LogSerial.print(strHMScode); 406 | if (printerVariables.overridestage != printerVariables.stage) 407 | { 408 | LogSerial.println(F(" **")); 409 | } 410 | else 411 | { 412 | LogSerial.println(F("")); 413 | } 414 | } 415 | else 416 | { 417 | LogSerial.println(F("NULL")); 418 | printerVariables.overridestage = 999; 419 | } 420 | } 421 | Changed = true; 422 | } 423 | } 424 | 425 | if (Changed == true) 426 | { 427 | printerConfig.inactivityStartms = millis(); // restart idle timer 428 | printerConfig.isIdleOFFActive = false; 429 | if (printerConfig.debuging) 430 | { 431 | LogSerial.println(F("Change from mqtt")); 432 | } 433 | printerConfig.maintMode_update = true; 434 | printerConfig.discoMode_update = true; 435 | printerConfig.replicate_update = true; 436 | printerConfig.testcolor_update = true; 437 | 438 | updateleds(); 439 | } 440 | } 441 | else 442 | { 443 | LogSerial.println(F("Deserialize error while parsing mqtt")); 444 | return; 445 | } 446 | } 447 | 448 | void mqttCallback(char *topic, byte *payload, unsigned int length) 449 | { 450 | ParseCallback(topic, (byte *)stream.get_buffer(), stream.current_length()); 451 | stream.flush(); 452 | } 453 | 454 | void setupMqtt() 455 | { 456 | clientId += String(random(0xffff), HEX); 457 | LogSerial.print(F("Setting up MQTT with Bambu Lab Printer IP address: ")); 458 | LogSerial.println(printerConfig.printerIP); 459 | 460 | device_topic = String("device/") + printerConfig.serialNumber; 461 | report_topic = device_topic + String("/report"); 462 | 463 | wifiSecureClient.setInsecure(); 464 | wifiSecureClient.setTimeout(3); 465 | mqttClient.setSocketTimeout(3); 466 | mqttClient.setBufferSize(1024); 467 | mqttClient.setServer(printerConfig.printerIP, 8883); 468 | mqttClient.setStream(stream); 469 | mqttClient.setCallback(mqttCallback); 470 | 471 | LogSerial.println(F("Finished setting up MQTT")); 472 | 473 | if (mqttTaskHandle == NULL) 474 | { 475 | BaseType_t result; 476 | 477 | #if CONFIG_FREERTOS_UNICORE 478 | result = xTaskCreate( 479 | mqttTask, 480 | "mqttTask", 481 | 6144, 482 | NULL, 483 | 1, 484 | &mqttTaskHandle 485 | ); 486 | #else 487 | result = xTaskCreatePinnedToCore( 488 | mqttTask, 489 | "mqttTask", 490 | 6144, 491 | NULL, 492 | 1, 493 | &mqttTaskHandle, 494 | 1 // Core 1 (App Core) 495 | ); 496 | #endif 497 | 498 | if (result == pdPASS) 499 | { 500 | LogSerial.println(F("MQTT task successfully started")); 501 | } 502 | else 503 | { 504 | LogSerial.println(F("Failed to create MQTT task!")); 505 | } 506 | } 507 | } 508 | 509 | 510 | void mqttloop() 511 | { 512 | if (WiFi.status() != WL_CONNECTED || WiFi.getMode() != WIFI_MODE_STA) 513 | { 514 | // Abort MQTT connection attempt when no Wifi 515 | return; 516 | } 517 | if (!mqttClient.connected()) 518 | { 519 | printerVariables.online = false; 520 | // Only sent the timer from the first instance of a MQTT disconnect 521 | if (printerVariables.disconnectMQTTms == 0) 522 | { 523 | printerVariables.disconnectMQTTms = millis(); 524 | // Record last time MQTT dropped connection 525 | LogSerial.println(F("MQTT dropped during mqttloop")); 526 | ParseMQTTState(mqttClient.state()); 527 | } 528 | //delay(500); 529 | connectMqtt(); 530 | delay(32); 531 | return; 532 | } 533 | else 534 | { 535 | printerVariables.disconnectMQTTms = 0; 536 | } 537 | mqttClient.loop(); 538 | delay(10); 539 | } 540 | 541 | #endif 542 | -------------------------------------------------------------------------------- /src/blled/mqttparsingutility.h: -------------------------------------------------------------------------------- 1 | #ifndef _MQTTPARSERUTILITY 2 | #define _MQTTPARSERUTILITY 3 | 4 | #include "types.h" 5 | 6 | String ParseHMSSeverity(int code){ // Provided by WolfWithSword 7 | int parsedcode (code>>16); 8 | switch (parsedcode){ 9 | case 1: 10 | return F("Fatal"); 11 | case 2: 12 | return F("Serious"); 13 | case 3: 14 | return F("Common"); 15 | case 4: 16 | return F("Info"); 17 | default:; 18 | } 19 | return ""; 20 | } 21 | 22 | void ParseMQTTState(int code){ 23 | switch (code) 24 | { 25 | case -4: // MQTT_CONNECTION_TIMEOUT 26 | LogSerial.println(F("MQTT TIMEOUT")); 27 | break; 28 | case -3: // MQTT_CONNECTION_LOST 29 | LogSerial.println(F("MQTT CONNECTION_LOST")); 30 | break; 31 | case -2: // MQTT_CONNECT_FAILED 32 | LogSerial.println(F("MQTT CONNECT_FAILED")); 33 | break; 34 | case -1: // MQTT_DISCONNECTED 35 | LogSerial.println(F("MQTT DISCONNECTED")); 36 | break; 37 | case 0: // MQTT_CONNECTED 38 | LogSerial.println(F("MQTT CONNECTED")); 39 | break; 40 | case 1: // MQTT_CONNECT_BAD_PROTOCOL 41 | LogSerial.println(F("MQTT BAD PROTOCOL")); 42 | break; 43 | case 2: // MQTT_CONNECT_BAD_CLIENT_ID 44 | LogSerial.println(F("MQTT BAD CLIENT ID")); 45 | break; 46 | case 3: // MQTT_CONNECT_UNAVAILABLE 47 | LogSerial.println(F("MQTT UNAVAILABLE")); 48 | break; 49 | case 4: // MQTT_CONNECT_BAD_CREDENTIALS 50 | LogSerial.println(F("MQTT BAD CREDENTIALS")); 51 | break; 52 | case 5: // MQTT UNAUTHORIZED 53 | LogSerial.println(F("MQTT UNAUTHORIZED")); 54 | break; 55 | } 56 | } 57 | 58 | 59 | #endif -------------------------------------------------------------------------------- /src/blled/serialmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLLEDSERIAL_MANAGER 2 | #define _BLLEDSERIAL_MANAGER 3 | 4 | #include 5 | #include 6 | 7 | #include "filesystem.h" 8 | #include "types.h" 9 | 10 | void setupSerial(){ 11 | while (!Serial); 12 | } 13 | 14 | void serialLoop(){ 15 | if (Serial.available() > 0) { 16 | String input = Serial.readStringUntil('\n'); 17 | JsonDocument doc; 18 | deserializeJson(doc, input); 19 | if (doc["ssid"].is() && doc["pass"].is()) { 20 | Serial.print(F("SSID ")); 21 | Serial.println(doc["ssid"].as()); 22 | Serial.print(F("PASS ")); 23 | Serial.println(doc["pass"].as()); 24 | 25 | Serial.println(doc["printerip"].as()); 26 | Serial.println(doc["printercode"].as()); 27 | Serial.println(doc["printerserial"].as()); 28 | 29 | strcpy(globalVariables.SSID, doc["ssid"]); 30 | strcpy(globalVariables.APPW, doc["pass"]); 31 | 32 | strcpy(printerConfig.printerIP, doc["printerip"]); 33 | strcpy(printerConfig.accessCode, doc["printercode"]); 34 | strcpy(printerConfig.serialNumber, doc["printerserial"]); 35 | 36 | saveFileSystem(); 37 | Serial.println(F("Restarting Device")); 38 | delay(1000); 39 | ESP.restart(); 40 | } 41 | 42 | }; 43 | delay(10); 44 | } 45 | 46 | #endif -------------------------------------------------------------------------------- /src/blled/ssdp.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLLEDSSDP 2 | #define _BLLEDSSDP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // this code makes the blled discoverable for the xtouch project. 10 | 11 | void start_ssdp(){ 12 | SSDP.setSchemaURL("description.xml"); 13 | SSDP.setHTTPPort(80); 14 | SSDP.setDeviceType("urn:schemas-upnp-org:device:DimmableLight:1"); 15 | SSDP.setName("BLLED Controller"); 16 | SSDP.setSerialNumber(WiFi.macAddress().c_str()); 17 | SSDP.setURL("/"); 18 | SSDP.setModelName("BLLED ESP32"); 19 | SSDP.setModelNumber("1.0"); 20 | SSDP.setManufacturer("DutchDeveloper"); 21 | SSDP.setManufacturerURL("https://dutchdevelop.com"); 22 | SSDP.begin(); 23 | } 24 | 25 | #endif -------------------------------------------------------------------------------- /src/blled/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _TYPES 2 | #define _TYPES 3 | 4 | #ifdef __cplusplus 5 | extern "C" 6 | { 7 | #endif 8 | 9 | typedef struct COLORStruct { 10 | short r; 11 | short g; 12 | short b; 13 | short ww; 14 | short cw; 15 | char RGBhex[8]; 16 | } COLOR; 17 | 18 | 19 | typedef struct PrinterVaraiblesStruct{ 20 | String parsedHMSlevel = ""; 21 | uint64_t parsedHMScode = 0; //8 bytes per code stored 22 | String gcodeState = "FINISH"; //Initialised to Finish so the logic doesn't 23 | //assume a Print has just finished and needs 24 | //to wait for a door interaction to continue 25 | int stage = 0; 26 | int overridestage = 999; 27 | bool printerledstate = true; 28 | bool hmsstate = false; 29 | bool online = false; 30 | bool finished = false; 31 | bool initalisedLEDs = false; 32 | //Time since 33 | unsigned long disconnectMQTTms = 0; 34 | 35 | //PrinterType 36 | bool isP1Printer = false; //Is this a P1 Printer without lidar or door switch 37 | //Door Monitoring 38 | bool useDoorSwtich = true; //DoorSwitch to be used for Actions? 39 | bool doorOpen = false; // Current State of Door 40 | bool doorSwitchTriggered = false; // Has door been closed twice within 6 seconds? 41 | bool waitingForDoor = false; // Are we waiting for the door to be actuated? 42 | unsigned long lastdoorClosems = 0; // Last time door was opened 43 | unsigned long lastdoorOpenms = 0; // Last time door was closed 44 | } PrinterVariables; 45 | 46 | typedef struct SecurityVariables{ 47 | // Security 48 | char HTTPUser[40] = ""; //http basic auth username 49 | char HTTPPass[40] = ""; //http basic auth password 50 | }SecurityVariables; 51 | SecurityVariables securityVariables; 52 | 53 | PrinterVariables printerVariables; 54 | 55 | typedef struct GlobalVariablesStruct{ 56 | char SSID[32]; 57 | char APPW[64]; 58 | String FWVersion = STRVERSION; 59 | String Host = "BLLED"; 60 | bool started = false; 61 | } GlobalVariables; 62 | 63 | GlobalVariables globalVariables; 64 | 65 | typedef struct PrinterConfigStruct 66 | { 67 | char printerIP[16]; //BBLP IP Address - used for MQTT reports 68 | char accessCode[9]; //BBLP Access Code - used for MQTT reports 69 | char serialNumber[16]; //BBLP Serial Number - used for MQTT reports 70 | 71 | char BSSID[18]; //Nominated AP to connect to (Useful if multiple accesspoints with same name) 72 | int brightness = 20; //Brightness of LEDS - Default to 20% in case user use LED's that draw too much power for their PS 73 | bool rescanWiFiNetwork = false; //Scans available WiFi networks for strongest signal 74 | // LED Behaviour (Choose One) 75 | bool maintMode = false; //White lights on, even if printer is unpowered 76 | bool maintMode_update = true; 77 | bool discoMode = false; //Cycles through RGB colors slowly for 'pretty' timelapse movie 78 | bool discoMode_update = true; 79 | bool replicatestate = true; //LED will be on if the BBPL Light is on 80 | bool replicate_update = true; //LED will be on if the BBPL Light is on 81 | COLOR runningColor; //Running Color (Default if no issues) 82 | bool testcolorEnabled = false; 83 | bool testcolor_update= true; //When updateleds() is run, should the TEST LEDS be set? 84 | COLOR testColor; //Test Color 85 | bool debugwifi = false; //Changes LED to a color range that represents WiFi signal Strength 86 | // Options 87 | bool finishindication = true; //Enable / Disable 88 | COLOR finishColor; //Set Finish Color 89 | bool finishExit = true; //True = use Door / False = use Timer 90 | bool finish_check = false; //When updateleds() is run, should the TEST LEDS be set? 91 | unsigned long finishStartms = 0; // Time the finish countdown is measured from 92 | int finishTimeOut = 600000; //300000 = 5 mins 93 | 94 | //Inactivity Timout 95 | bool inactivityEnabled = true; 96 | bool isIdleOFFActive = false; // Are the lights out due to inactivity Timeout? 97 | unsigned long inactivityStartms = 0; // Time the inactivity countdown is measured from 98 | int inactivityTimeOut = 3600000; // 1800000 = 30mins / 600000 = 10mins / 60000 = 1mins 99 | // Debugging 100 | bool debuging = false; //Debugging for all interactions through functions 101 | bool debugingchange = true; //Default debugging level - to shows onChange 102 | bool mqttdebug = false; //Writes each packet from BBLP to the serial log 103 | //Custom Colors for events using lidar 104 | COLOR stage14Color; 105 | COLOR stage1Color; 106 | COLOR stage8Color; 107 | COLOR stage9Color; 108 | COLOR stage10Color; 109 | // Customise LED Colors 110 | bool errordetection = true; //Utilises Error Colors when BBLP give an error 111 | COLOR wifiRGB; 112 | COLOR pauseRGB; 113 | COLOR firstlayerRGB; 114 | COLOR nozzleclogRGB; 115 | COLOR hmsSeriousRGB; 116 | COLOR hmsFatalRGB; 117 | COLOR filamentRunoutRGB; 118 | COLOR frontCoverRGB; 119 | COLOR nozzleTempRGB; 120 | COLOR bedTempRGB; 121 | 122 | //char webpagePassword[8]; 123 | } PrinterConfig; 124 | 125 | PrinterConfig printerConfig; 126 | 127 | #ifdef __cplusplus 128 | } /*extern "C"*/ 129 | #endif 130 | 131 | #endif -------------------------------------------------------------------------------- /src/blled/web-server.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLLEDWEB_SERVER 2 | #define _BLLEDWEB_SERVER 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "leds.h" 11 | #include "filesystem.h" 12 | 13 | AsyncWebServer webServer(80); 14 | AsyncWebSocket ws("/ws"); 15 | 16 | #include "../www/www.h" 17 | 18 | unsigned long lastWsPush = 0; 19 | const unsigned long wsPushInterval = 1000; // alle 1000ms 20 | 21 | bool isAuthorized(AsyncWebServerRequest *request) 22 | { 23 | if (strlen(securityVariables.HTTPUser) == 0 || strlen(securityVariables.HTTPPass) == 0) 24 | { 25 | return true; 26 | } 27 | return request->authenticate(securityVariables.HTTPUser, securityVariables.HTTPPass); 28 | } 29 | 30 | void handleSetup(AsyncWebServerRequest *request) 31 | { 32 | if (!isAuthorized(request)) 33 | { 34 | return request->requestAuthentication(); 35 | } 36 | AsyncWebServerResponse *response = request->beginResponse(200, setupPage_html_gz_mime, setupPage_html_gz, setupPage_html_gz_len); 37 | response->addHeader("Content-Encoding", "gzip"); 38 | request->send(response); 39 | } 40 | 41 | void handleOldSetup(AsyncWebServerRequest *request) 42 | { 43 | if (!isAuthorized(request)) 44 | { 45 | return request->requestAuthentication(); 46 | } 47 | AsyncWebServerResponse *response = request->beginResponse(200, setupPageOld_html_gz_mime, setupPageOld_html_gz, setupPageOld_html_gz_len); 48 | response->addHeader("Content-Encoding", "gzip"); 49 | request->send(response); 50 | } 51 | 52 | void handleUpdatePage(AsyncWebServerRequest *request) 53 | { 54 | if (!isAuthorized(request)) 55 | { 56 | return request->requestAuthentication(); 57 | } 58 | AsyncWebServerResponse *response = request->beginResponse(200, updatePage_html_gz_mime, updatePage_html_gz, updatePage_html_gz_len); 59 | response->addHeader("Content-Encoding", "gzip"); 60 | request->send(response); 61 | } 62 | 63 | void handleGetIcon(AsyncWebServerRequest *request) 64 | { 65 | if (!isAuthorized(request)) 66 | { 67 | return request->requestAuthentication(); 68 | } 69 | AsyncWebServerResponse *response = request->beginResponse(200, blled_svg_gz_mime, blled_svg_gz, blled_svg_gz_len); 70 | response->addHeader("Content-Encoding", "gzip"); 71 | request->send(response); 72 | } 73 | 74 | void handleGetfavicon(AsyncWebServerRequest *request) 75 | { 76 | if (!isAuthorized(request)) 77 | { 78 | return request->requestAuthentication(); 79 | } 80 | AsyncWebServerResponse *response = request->beginResponse(200, favicon_png_gz_mime, favicon_png_gz, favicon_png_gz_len); 81 | response->addHeader("Content-Encoding", "gzip"); 82 | request->send(response); 83 | } 84 | 85 | void handleGetPCC(AsyncWebServerRequest *request) 86 | { 87 | if (!isAuthorized(request)) 88 | { 89 | return request->requestAuthentication(); 90 | } 91 | AsyncWebServerResponse *response = request->beginResponse(200, particleCanvas_js_gz_mime, (const uint8_t *)particleCanvas_js_gz, particleCanvas_js_gz_len); 92 | response->addHeader("Content-Encoding", "gzip"); 93 | request->send(response); 94 | } 95 | 96 | void handleGetConfig(AsyncWebServerRequest *request) 97 | { 98 | if (!isAuthorized(request)) 99 | { 100 | return request->requestAuthentication(); 101 | } 102 | 103 | JsonDocument doc; 104 | 105 | doc["firmwareversion"] = globalVariables.FWVersion.c_str(); 106 | doc["wifiStrength"] = WiFi.RSSI(); 107 | doc["ip"] = printerConfig.printerIP; 108 | doc["code"] = printerConfig.accessCode; 109 | doc["id"] = printerConfig.serialNumber; 110 | doc["apMAC"] = printerConfig.BSSID; 111 | doc["brightness"] = printerConfig.brightness; 112 | doc["maintMode"] = printerConfig.maintMode; 113 | doc["discoMode"] = printerConfig.discoMode; 114 | doc["replicateled"] = printerConfig.replicatestate; 115 | doc["runningRGB"] = printerConfig.runningColor.RGBhex; 116 | doc["runningWW"] = printerConfig.runningColor.ww; 117 | doc["runningCW"] = printerConfig.runningColor.cw; 118 | doc["showtestcolor"] = printerConfig.testcolorEnabled; 119 | doc["testRGB"] = printerConfig.testColor.RGBhex; 120 | doc["testWW"] = printerConfig.testColor.ww; 121 | doc["testCW"] = printerConfig.testColor.cw; 122 | doc["debugwifi"] = printerConfig.debugwifi; 123 | doc["finishindication"] = printerConfig.finishindication; 124 | doc["finishColor"] = printerConfig.finishColor.RGBhex; 125 | doc["finishWW"] = printerConfig.finishColor.ww; 126 | doc["finishCW"] = printerConfig.finishColor.cw; 127 | doc["finishExit"] = printerConfig.finishExit; 128 | doc["finishTimerMins"] = (int)(printerConfig.finishTimeOut / 60000); 129 | doc["inactivityEnabled"] = printerConfig.inactivityEnabled; 130 | doc["inactivityMins"] = (int)(printerConfig.inactivityTimeOut / 60000); 131 | doc["debuging"] = printerConfig.debuging; 132 | doc["debugingchange"] = printerConfig.debugingchange; 133 | doc["mqttdebug"] = printerConfig.mqttdebug; 134 | doc["p1Printer"] = printerVariables.isP1Printer; 135 | doc["doorSwitch"] = printerVariables.useDoorSwtich; 136 | doc["stage14RGB"] = printerConfig.stage14Color.RGBhex; 137 | doc["stage14WW"] = printerConfig.stage14Color.ww; 138 | doc["stage14CW"] = printerConfig.stage14Color.cw; 139 | doc["stage1RGB"] = printerConfig.stage1Color.RGBhex; 140 | doc["stage1WW"] = printerConfig.stage1Color.ww; 141 | doc["stage1CW"] = printerConfig.stage1Color.cw; 142 | doc["stage8RGB"] = printerConfig.stage8Color.RGBhex; 143 | doc["stage8WW"] = printerConfig.stage8Color.ww; 144 | doc["stage8CW"] = printerConfig.stage8Color.cw; 145 | doc["stage9RGB"] = printerConfig.stage9Color.RGBhex; 146 | doc["stage9WW"] = printerConfig.stage9Color.ww; 147 | doc["stage9CW"] = printerConfig.stage9Color.cw; 148 | doc["stage10RGB"] = printerConfig.stage10Color.RGBhex; 149 | doc["stage10WW"] = printerConfig.stage10Color.ww; 150 | doc["stage10CW"] = printerConfig.stage10Color.cw; 151 | doc["errordetection"] = printerConfig.errordetection; 152 | doc["wifiRGB"] = printerConfig.wifiRGB.RGBhex; 153 | doc["wifiWW"] = printerConfig.wifiRGB.ww; 154 | doc["wifiCW"] = printerConfig.wifiRGB.cw; 155 | doc["pauseRGB"] = printerConfig.pauseRGB.RGBhex; 156 | doc["pauseWW"] = printerConfig.pauseRGB.ww; 157 | doc["pauseCW"] = printerConfig.pauseRGB.cw; 158 | doc["firstlayerRGB"] = printerConfig.firstlayerRGB.RGBhex; 159 | doc["firstlayerWW"] = printerConfig.firstlayerRGB.ww; 160 | doc["firstlayerCW"] = printerConfig.firstlayerRGB.cw; 161 | doc["nozzleclogRGB"] = printerConfig.nozzleclogRGB.RGBhex; 162 | doc["nozzleclogWW"] = printerConfig.nozzleclogRGB.ww; 163 | doc["nozzleclogCW"] = printerConfig.nozzleclogRGB.cw; 164 | doc["hmsSeriousRGB"] = printerConfig.hmsSeriousRGB.RGBhex; 165 | doc["hmsSeriousWW"] = printerConfig.hmsSeriousRGB.ww; 166 | doc["hmsSeriousCW"] = printerConfig.hmsSeriousRGB.cw; 167 | doc["hmsFatalRGB"] = printerConfig.hmsFatalRGB.RGBhex; 168 | doc["hmsFatalWW"] = printerConfig.hmsFatalRGB.ww; 169 | doc["hmsFatalCW"] = printerConfig.hmsFatalRGB.cw; 170 | doc["filamentRunoutRGB"] = printerConfig.filamentRunoutRGB.RGBhex; 171 | doc["filamentRunoutWW"] = printerConfig.filamentRunoutRGB.ww; 172 | doc["filamentRunoutCW"] = printerConfig.filamentRunoutRGB.cw; 173 | doc["frontCoverRGB"] = printerConfig.frontCoverRGB.RGBhex; 174 | doc["frontCoverWW"] = printerConfig.frontCoverRGB.ww; 175 | doc["frontCoverCW"] = printerConfig.frontCoverRGB.cw; 176 | doc["nozzleTempRGB"] = printerConfig.nozzleTempRGB.RGBhex; 177 | doc["nozzleTempWW"] = printerConfig.nozzleTempRGB.ww; 178 | doc["nozzleTempCW"] = printerConfig.nozzleTempRGB.cw; 179 | doc["bedTempRGB"] = printerConfig.bedTempRGB.RGBhex; 180 | doc["bedTempWW"] = printerConfig.bedTempRGB.ww; 181 | doc["bedTempCW"] = printerConfig.bedTempRGB.cw; 182 | 183 | String jsonString; 184 | serializeJson(doc, jsonString); 185 | request->send(200, "application/json", jsonString); 186 | } 187 | 188 | void handlePrinterConfigJson(AsyncWebServerRequest *request) 189 | { 190 | if (!isAuthorized(request)) 191 | { 192 | return request->requestAuthentication(); 193 | } 194 | JsonDocument doc; 195 | doc["ssid"] = globalVariables.SSID; 196 | doc["pass"] = globalVariables.APPW; 197 | doc["printerIP"] = printerConfig.printerIP; 198 | doc["printerSerial"] = printerConfig.serialNumber; 199 | doc["accessCode"] = printerConfig.accessCode; 200 | doc["webUser"] = securityVariables.HTTPUser; 201 | doc["webPass"] = securityVariables.HTTPPass; 202 | 203 | String json; 204 | serializeJson(doc, json); 205 | request->send(200, "application/json", json); 206 | } 207 | 208 | void handleStyleCss(AsyncWebServerRequest *request) 209 | { 210 | if (!isAuthorized(request)) 211 | { 212 | return request->requestAuthentication(); 213 | } 214 | AsyncWebServerResponse *response = request->beginResponse(200, style_css_gz_mime, style_css_gz, style_css_gz_len); 215 | response->addHeader("Content-Encoding", "gzip"); 216 | request->send(response); 217 | } 218 | 219 | void handleSubmitConfig(AsyncWebServerRequest *request) 220 | { 221 | if (!isAuthorized(request)) 222 | { 223 | return request->requestAuthentication(); 224 | } 225 | 226 | auto getSafeParamValue = [](AsyncWebServerRequest *req, const char *name, const char *fallback = "") -> String 227 | { 228 | return req->hasParam(name, true) ? req->getParam(name, true)->value() : fallback; 229 | }; 230 | 231 | auto getSafeParamInt = [](AsyncWebServerRequest *req, const char *name, int fallback = 0) -> int 232 | { 233 | return req->hasParam(name, true) ? req->getParam(name, true)->value().toInt() : fallback; 234 | }; 235 | 236 | printerConfig.brightness = getSafeParamInt(request, "brightnessslider"); 237 | printerConfig.rescanWiFiNetwork = request->hasParam("rescanWiFiNetwork", true); 238 | printerConfig.maintMode = request->hasParam("maintMode", true); 239 | printerConfig.discoMode = request->hasParam("discoMode", true); 240 | printerConfig.replicatestate = request->hasParam("replicateLedState", true); 241 | printerConfig.runningColor = hex2rgb(getSafeParamValue(request, "runningRGB").c_str(), getSafeParamInt(request, "runningWW"), getSafeParamInt(request, "runningCW")); 242 | printerConfig.testcolorEnabled = request->hasParam("showtestcolor", true); 243 | printerConfig.testColor = hex2rgb(getSafeParamValue(request, "testRGB").c_str(), getSafeParamInt(request, "testWW"), getSafeParamInt(request, "testCW")); 244 | printerConfig.debugwifi = request->hasParam("debugwifi", true); 245 | printerConfig.finishindication = request->hasParam("finishIndication", true); 246 | printerConfig.finishColor = hex2rgb(getSafeParamValue(request, "finishColor").c_str(), getSafeParamInt(request, "finishWW"), getSafeParamInt(request, "finishCW")); 247 | printerConfig.finishExit = !request->hasParam("finishEndTimer", true); 248 | printerConfig.finishTimeOut = getSafeParamInt(request, "finishTimerMins") * 60000; 249 | printerConfig.inactivityEnabled = request->hasParam("inactivityEnabled", true); 250 | printerConfig.inactivityTimeOut = getSafeParamInt(request, "inactivityMins") * 60000; 251 | printerConfig.debuging = request->hasParam("debuging", true); 252 | printerConfig.debugingchange = request->hasParam("debugingchange", true); 253 | printerConfig.mqttdebug = request->hasParam("mqttdebug", true); 254 | printerVariables.isP1Printer = request->hasParam("p1Printer", true); 255 | printerVariables.useDoorSwtich = request->hasParam("doorSwitch", true); 256 | 257 | printerConfig.stage14Color = hex2rgb(getSafeParamValue(request, "stage14RGB").c_str(), getSafeParamInt(request, "stage14WW"), getSafeParamInt(request, "stage14CW")); 258 | printerConfig.stage1Color = hex2rgb(getSafeParamValue(request, "stage1RGB").c_str(), getSafeParamInt(request, "stage1WW"), getSafeParamInt(request, "stage1CW")); 259 | printerConfig.stage8Color = hex2rgb(getSafeParamValue(request, "stage8RGB").c_str(), getSafeParamInt(request, "stage8WW"), getSafeParamInt(request, "stage8CW")); 260 | printerConfig.stage9Color = hex2rgb(getSafeParamValue(request, "stage9RGB").c_str(), getSafeParamInt(request, "stage9WW"), getSafeParamInt(request, "stage9CW")); 261 | printerConfig.stage10Color = hex2rgb(getSafeParamValue(request, "stage10RGB").c_str(), getSafeParamInt(request, "stage10WW"), getSafeParamInt(request, "stage10CW")); 262 | printerConfig.errordetection = request->hasParam("errorDetection", true); 263 | printerConfig.wifiRGB = hex2rgb(getSafeParamValue(request, "wifiRGB").c_str(), getSafeParamInt(request, "wifiWW"), getSafeParamInt(request, "wifiCW")); 264 | printerConfig.pauseRGB = hex2rgb(getSafeParamValue(request, "pauseRGB").c_str(), getSafeParamInt(request, "pauseWW"), getSafeParamInt(request, "pauseCW")); 265 | printerConfig.firstlayerRGB = hex2rgb(getSafeParamValue(request, "firstlayerRGB").c_str(), getSafeParamInt(request, "firstlayerWW"), getSafeParamInt(request, "firstlayerCW")); 266 | printerConfig.nozzleclogRGB = hex2rgb(getSafeParamValue(request, "nozzleclogRGB").c_str(), getSafeParamInt(request, "nozzleclogWW"), getSafeParamInt(request, "nozzleclogCW")); 267 | printerConfig.hmsSeriousRGB = hex2rgb(getSafeParamValue(request, "hmsSeriousRGB").c_str(), getSafeParamInt(request, "hmsSeriousWW"), getSafeParamInt(request, "hmsSeriousCW")); 268 | printerConfig.hmsFatalRGB = hex2rgb(getSafeParamValue(request, "hmsFatalRGB").c_str(), getSafeParamInt(request, "hmsFatalWW"), getSafeParamInt(request, "hmsFatalCW")); 269 | printerConfig.filamentRunoutRGB = hex2rgb(getSafeParamValue(request, "filamentRunoutRGB").c_str(), getSafeParamInt(request, "filamentRunoutWW"), getSafeParamInt(request, "filamentRunoutCW")); 270 | printerConfig.frontCoverRGB = hex2rgb(getSafeParamValue(request, "frontCoverRGB").c_str(), getSafeParamInt(request, "frontCoverWW"), getSafeParamInt(request, "frontCoverCW")); 271 | printerConfig.nozzleTempRGB = hex2rgb(getSafeParamValue(request, "nozzleTempRGB").c_str(), getSafeParamInt(request, "nozzleTempWW"), getSafeParamInt(request, "nozzleTempCW")); 272 | printerConfig.bedTempRGB = hex2rgb(getSafeParamValue(request, "bedTempRGB").c_str(), getSafeParamInt(request, "bedTempWW"), getSafeParamInt(request, "bedTempCW")); 273 | 274 | saveFileSystem(); 275 | LogSerial.println(F("Packet received from setuppage")); 276 | printerConfig.inactivityStartms = millis(); 277 | printerConfig.isIdleOFFActive = false; 278 | printerConfig.replicate_update = true; 279 | printerConfig.maintMode_update = true; 280 | printerConfig.discoMode_update = true; 281 | printerConfig.testcolor_update = true; 282 | updateleds(); 283 | request->send(200, "text/plain", "OK"); 284 | } 285 | 286 | void sendJsonToAll(JsonDocument &doc) 287 | { 288 | String jsonString; 289 | serializeJson(doc, jsonString); 290 | ws.textAll(jsonString); 291 | } 292 | 293 | void handleWiFiScan(AsyncWebServerRequest *request) 294 | { 295 | JsonDocument doc; 296 | JsonArray networks = doc["networks"].to(); 297 | 298 | int n = WiFi.scanNetworks(); 299 | for (int i = 0; i < n; ++i) 300 | { 301 | JsonObject net = networks.add(); 302 | net["ssid"] = WiFi.SSID(i); 303 | net["rssi"] = WiFi.RSSI(i); 304 | net["enc"] = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? false : true; 305 | } 306 | 307 | String json; 308 | serializeJson(doc, json); 309 | request->send(200, "application/json", json); 310 | } 311 | 312 | void handleWiFiSetupPage(AsyncWebServerRequest *request) 313 | { 314 | AsyncWebServerResponse *response = request->beginResponse(200, wifiSetup_html_gz_mime, wifiSetup_html_gz, wifiSetup_html_gz_len); 315 | response->addHeader("Content-Encoding", "gzip"); 316 | request->send(response); 317 | } 318 | 319 | void handleSubmitWiFi(AsyncWebServerRequest *request) 320 | { 321 | // Check for SSID + PASS optional handling 322 | bool hasSSID = request->hasParam("ssid", true); 323 | bool hasPASS = request->hasParam("pass", true); 324 | 325 | if (hasSSID && hasPASS) 326 | { 327 | String ssid = request->getParam("ssid", true)->value(); 328 | String pass = request->getParam("pass", true)->value(); 329 | ssid.trim(); 330 | pass.trim(); 331 | 332 | if (ssid.length() > 0 && pass.length() > 0) 333 | { 334 | LogSerial.println(F("[WiFiSetup] Updating WiFi credentials:")); 335 | LogSerial.print(F("SSID: ")); 336 | LogSerial.println(ssid); 337 | LogSerial.print(F("Password: ")); 338 | LogSerial.println(pass); 339 | 340 | strlcpy(globalVariables.SSID, ssid.c_str(), sizeof(globalVariables.SSID)); 341 | strlcpy(globalVariables.APPW, pass.c_str(), sizeof(globalVariables.APPW)); 342 | } 343 | else 344 | { 345 | LogSerial.println(F("[WiFiSetup] Empty SSID or PASS received → ignoring WiFi update.")); 346 | } 347 | } 348 | else 349 | { 350 | LogSerial.println(F("[WiFiSetup] No SSID or PASS provided → keeping existing WiFi credentials.")); 351 | } 352 | 353 | // Optional other fields (printerIP, printerSerial, etc.) 354 | String printerIP = request->hasParam("printerIP", true) ? request->getParam("printerIP", true)->value() : ""; 355 | String printerSerial = request->hasParam("printerSerial", true) ? request->getParam("printerSerial", true)->value() : ""; 356 | String accessCode = request->hasParam("accessCode", true) ? request->getParam("accessCode", true)->value() : ""; 357 | String webUser = request->hasParam("webUser", true) ? request->getParam("webUser", true)->value() : ""; 358 | String webPass = request->hasParam("webPass", true) ? request->getParam("webPass", true)->value() : ""; 359 | 360 | if (printerIP.length() > 0) 361 | strlcpy(printerConfig.printerIP, printerIP.c_str(), sizeof(printerConfig.printerIP)); 362 | if (printerSerial.length() > 0) 363 | strlcpy(printerConfig.serialNumber, printerSerial.c_str(), sizeof(printerConfig.serialNumber)); 364 | if (accessCode.length() > 0) 365 | strlcpy(printerConfig.accessCode, accessCode.c_str(), sizeof(printerConfig.accessCode)); 366 | 367 | strlcpy(securityVariables.HTTPUser, webUser.c_str(), sizeof(securityVariables.HTTPUser)); 368 | strlcpy(securityVariables.HTTPPass, webPass.c_str(), sizeof(securityVariables.HTTPPass)); 369 | 370 | saveFileSystem(); 371 | 372 | request->send(200, "text/plain", "Settings saved, restarting..."); 373 | shouldRestart = true; 374 | restartRequestTime = millis(); 375 | } 376 | 377 | void websocketLoop() 378 | { 379 | if (ws.count() == 0) 380 | return; 381 | if (millis() - lastWsPush > wsPushInterval) 382 | { 383 | lastWsPush = millis(); 384 | 385 | JsonDocument doc; 386 | doc["wifi_rssi"] = WiFi.RSSI(); 387 | doc["ip"] = WiFi.localIP().toString(); 388 | doc["uptime"] = millis() / 1000; 389 | doc["doorOpen"] = printerVariables.doorOpen; 390 | doc["printerConnection"] = printerVariables.online; 391 | doc["clients"] = ws.count(); 392 | doc["stg_cur"] = printerVariables.stage; 393 | sendJsonToAll(doc); 394 | } 395 | } 396 | 397 | void handleConfigPage(AsyncWebServerRequest *request) 398 | { 399 | if (!isAuthorized(request)) 400 | return request->requestAuthentication(); 401 | AsyncWebServerResponse *response = request->beginResponse(200, backupRestore_html_gz_mime, backupRestore_html_gz, backupRestore_html_gz_len); 402 | response->addHeader("Content-Encoding", "gzip"); 403 | request->send(response); 404 | } 405 | 406 | void handleDownloadConfigFile(AsyncWebServerRequest *request) 407 | { 408 | if (!isAuthorized(request)) 409 | { 410 | return request->requestAuthentication(); 411 | } 412 | 413 | if (!LittleFS.exists(configPath)) 414 | { 415 | request->send(404, "text/plain", "Config file not found"); 416 | return; 417 | } 418 | 419 | File configFile = LittleFS.open(configPath, "r"); 420 | if (!configFile) 421 | { 422 | request->send(500, "text/plain", "Failed to open config file"); 423 | return; 424 | } 425 | 426 | JsonDocument doc; 427 | DeserializationError error = deserializeJson(doc, configFile); 428 | configFile.close(); 429 | 430 | if (error) 431 | { 432 | request->send(500, "text/plain", "Failed to parse config file"); 433 | return; 434 | } 435 | 436 | String jsonString; 437 | serializeJsonPretty(doc, jsonString); 438 | 439 | AsyncWebServerResponse *response = request->beginResponse(200, "application/json", jsonString); 440 | response->addHeader("Content-Disposition", "attachment; filename=\"blledconfig.json\""); 441 | request->send(response); 442 | } 443 | 444 | 445 | void handleWebSerialPage(AsyncWebServerRequest *request) 446 | { 447 | if (!isAuthorized(request)) 448 | return request->requestAuthentication(); 449 | AsyncWebServerResponse *response = request->beginResponse(200, webSerialPage_html_gz_mime, webSerialPage_html_gz, webSerialPage_html_gz_len); 450 | response->addHeader("Content-Encoding", "gzip"); 451 | request->send(response); 452 | } 453 | 454 | 455 | void handleUploadConfigFileData(AsyncWebServerRequest *request, const String &filename, 456 | size_t index, uint8_t *data, size_t len, bool final) 457 | { 458 | static File uploadFile; 459 | 460 | if (!index) 461 | { 462 | LogSerial.println(F("[ConfigUpload] Start")); 463 | uploadFile = LittleFS.open(configPath, "w"); 464 | } 465 | if (uploadFile) 466 | { 467 | uploadFile.write(data, len); 468 | } 469 | if (final) 470 | { 471 | uploadFile.close(); 472 | LogSerial.println(F("[ConfigUpload] Finished")); 473 | } 474 | shouldRestart = true; 475 | restartRequestTime = millis(); 476 | } 477 | 478 | void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, 479 | void *arg, uint8_t *data, size_t len) 480 | { 481 | switch (type) 482 | { 483 | case WS_EVT_CONNECT: 484 | LogSerial.printf("[WS] Client connected: %u\n", client->id()); 485 | websocketLoop(); 486 | break; 487 | case WS_EVT_DISCONNECT: 488 | LogSerial.printf("[WS] Client disconnected: %u\n", client->id()); 489 | ws.cleanupClients(); 490 | break; 491 | case WS_EVT_DATA: 492 | LogSerial.printf("[WS] Data received from client %u\n", client->id()); 493 | break; 494 | case WS_EVT_PONG: 495 | LogSerial.printf("[WS] Pong received from %u\n", client->id()); 496 | break; 497 | case WS_EVT_ERROR: 498 | LogSerial.printf("[WS] Error on connection %u\n", client->id()); 499 | ws.cleanupClients(); 500 | break; 501 | } 502 | } 503 | 504 | void setupWebserver() 505 | { 506 | if (!MDNS.begin(globalVariables.Host.c_str())) 507 | { 508 | LogSerial.println(F("Error setting up MDNS responder!")); 509 | while (1) 510 | delay(500); 511 | } 512 | 513 | LogSerial.println(F("Setting up webserver")); 514 | 515 | webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) 516 | { 517 | if (WiFi.getMode() == WIFI_AP) { 518 | LogSerial.println(F("[WebServer] Captive Portal activ – redirect to /wifi")); 519 | request->redirect("/wifi"); 520 | } else { 521 | handleSetup(request); 522 | //handleOldSetup(request); 523 | } }); 524 | webServer.on("/old", HTTP_GET, handleOldSetup); 525 | webServer.on("/fwupdate", HTTP_GET, handleUpdatePage); 526 | webServer.on("/getConfig", HTTP_GET, handleGetConfig); 527 | webServer.on("/submitConfig", HTTP_POST, handleSubmitConfig); 528 | webServer.on("/blled.svg", HTTP_GET, handleGetIcon); 529 | webServer.on("/favicon.ico", HTTP_GET, handleGetfavicon); 530 | webServer.on("/particleCanvas.js", HTTP_GET, handleGetPCC); 531 | webServer.on("/config.json", HTTP_GET, handlePrinterConfigJson); 532 | webServer.on("/wifi", HTTP_GET, handleWiFiSetupPage); 533 | webServer.on("/wifiScan", HTTP_GET, handleWiFiScan); 534 | webServer.on("/submitWiFi", HTTP_POST, handleSubmitWiFi); 535 | webServer.on("/style.css", HTTP_GET, handleStyleCss); 536 | webServer.on("/backuprestore", HTTP_GET, handleConfigPage); 537 | webServer.on("/configfile.json", HTTP_GET, handleDownloadConfigFile); 538 | webServer.on("/webserial", HTTP_GET, handleWebSerialPage); 539 | webServer.on("/configrestore", HTTP_POST, [](AsyncWebServerRequest *request) 540 | { 541 | if (!isAuthorized(request)) { 542 | return request->requestAuthentication(); 543 | } 544 | request->send(200, "text/plain", "Config uploaded. Restarting..."); 545 | shouldRestart = true; 546 | restartRequestTime = millis(); }, [](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) 547 | { 548 | static File uploadFile; 549 | 550 | if (!index) { 551 | LogSerial.printf("[ConfigUpload] Start: %s\n", filename.c_str()); 552 | uploadFile = LittleFS.open(configPath, "w"); 553 | } 554 | if (uploadFile) { 555 | uploadFile.write(data, len); 556 | } 557 | if (final) { 558 | uploadFile.close(); 559 | LogSerial.println(F("[ConfigUpload] Finished")); 560 | } }); 561 | 562 | webServer.on("/update", HTTP_POST, [](AsyncWebServerRequest *request) 563 | { 564 | request->send(200, "text/plain", "OK"); 565 | LogSerial.println(F("OTA Upload done. Marking for restart.")); 566 | shouldRestart = true; 567 | restartRequestTime = millis(); }, [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) 568 | { 569 | if (!index) { 570 | LogSerial.printf("[OTA] Start: %s\n", filename.c_str()); 571 | if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { 572 | Update.printError(LogSerial); 573 | } 574 | } 575 | 576 | if (Update.write(data, len) != len) { 577 | Update.printError(LogSerial); 578 | } 579 | 580 | if (final) { 581 | if (Update.end(true)) { 582 | LogSerial.printf("[OTA] Success (%u bytes). Awaiting reboot...\n", index + len); 583 | } else { 584 | Update.printError(LogSerial); 585 | } 586 | } }); 587 | 588 | LogSerial.begin(&webServer); 589 | 590 | ws.onEvent(onWsEvent); 591 | webServer.addHandler(&ws); 592 | 593 | webServer.begin(); 594 | 595 | LogSerial.println(F("Webserver started")); 596 | } 597 | 598 | #endif 599 | -------------------------------------------------------------------------------- /src/blled/wifi-manager.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLLEDWIFI_MANAGER 2 | #define _BLLEDWIFI_MANAGER 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "filesystem.h" 9 | #include "types.h" 10 | 11 | #include 12 | DNSServer dnsServer; 13 | IPAddress apIP(192, 168, 4, 1); 14 | 15 | bool shouldSaveConfig = true; 16 | int connectionAttempts = 1; 17 | int wifimode = 0; 18 | uint8_t bssid[6] = {0}; 19 | 20 | void configModeCallback() { 21 | Serial.println(F("Entered config mode")); 22 | Serial.print(F("AP IP address: ")); 23 | Serial.println(WiFi.softAPIP()); 24 | } 25 | const char* wl_status_to_string(wl_status_t status) { 26 | switch (status) { 27 | case WL_NO_SHIELD: return "WL_NO_SHIELD"; 28 | case WL_IDLE_STATUS: return "WL_IDLE_STATUS"; 29 | case WL_NO_SSID_AVAIL: return "WL_NO_SSID_AVAIL"; 30 | case WL_SCAN_COMPLETED: return "WL_SCAN_COMPLETED"; 31 | case WL_CONNECTED: return "WL_CONNECTED"; 32 | case WL_CONNECT_FAILED: return "WL_CONNECT_FAILED"; 33 | case WL_CONNECTION_LOST: return "WL_CONNECTION_LOST"; 34 | case WL_DISCONNECTED: return "WL_DISCONNECTED"; 35 | } 36 | return "UNKNOWN"; 37 | } 38 | int str2mac(const char* mac, uint8_t* values){ 39 | if( 6 == sscanf( mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",&values[0], &values[1], &values[2],&values[3], &values[4], &values[5] ) ){ 40 | return 1; 41 | }else{ 42 | return 0; 43 | } 44 | } 45 | bool connectToWifi(){ 46 | Serial.println(F("-------------------------------------")); 47 | WiFi.mode(WIFI_STA); //ESP32 connects to an access point 48 | delay(10); 49 | WiFi.disconnect(); 50 | if(strlen(printerConfig.BSSID) == 0){ 51 | WiFi.begin(globalVariables.SSID, globalVariables.APPW); 52 | wifimode = 1; 53 | } 54 | else{ 55 | // Reference https://stackoverflow.com/questions/70415075/esp32-select-one-from-multiple-wifi-ap-with-same-name-ssid 56 | 57 | //Function to convert String MAC address to 6 byte array 58 | if(str2mac(printerConfig.BSSID,bssid)){ 59 | WiFi.begin(globalVariables.SSID, globalVariables.APPW, 0, bssid); 60 | wifimode = 0; 61 | } 62 | else{ 63 | if (printerConfig.debuging || printerConfig.debugingchange){ 64 | Serial.print(F("Parsing MAC Address Failed, reverting to ")); 65 | Serial.println(globalVariables.SSID); 66 | } 67 | WiFi.begin(globalVariables.SSID, globalVariables.APPW); 68 | wifimode = 1; 69 | } 70 | } 71 | 72 | wl_status_t status = WiFi.status(); 73 | while (status != WL_CONNECTED) { 74 | if(connectionAttempts > 10 && wifimode == 0){ 75 | WiFi.disconnect(); 76 | WiFi.begin(globalVariables.SSID, globalVariables.APPW); 77 | connectionAttempts = 1; 78 | wifimode = 1; 79 | Serial.println(F("Attempting to connect without specific BSSI")); 80 | } 81 | if(connectionAttempts > 10 && wifimode == 1){ 82 | WiFi.disconnect(); 83 | WiFi.begin(globalVariables.SSID); 84 | connectionAttempts = 1; 85 | wifimode = 2; 86 | if(strlen(printerConfig.BSSID) == 0) wifimode = 0; 87 | 88 | Serial.println(F("Attempting to connect to open network (no password)")); 89 | } 90 | if(connectionAttempts > 10 && wifimode == 2){ 91 | WiFi.disconnect(); 92 | WiFi.begin(globalVariables.SSID, globalVariables.APPW, 0, bssid); 93 | connectionAttempts = 1; 94 | wifimode = 0; 95 | Serial.println(F("Attempting to connect to wifi using BSSI")); 96 | } 97 | 98 | Serial.print(F("Connecting to WIFI.. Status check #")); 99 | Serial.print(connectionAttempts); 100 | Serial.print(F(" / 10 SSID: ")); 101 | Serial.print(globalVariables.SSID); 102 | if(strlen(printerConfig.BSSID) > 0){ 103 | Serial.print(F(" BSSID: ")); 104 | Serial.print(printerConfig.BSSID); 105 | } 106 | Serial.println(); 107 | 108 | if(status != WiFi.status()){ 109 | status = WiFi.status(); 110 | switch (status) 111 | { 112 | case WL_CONNECTED: 113 | case WL_IDLE_STATUS: 114 | case WL_CONNECT_FAILED: 115 | Serial.print(F("Wifi Status: ")); 116 | Serial.println(wl_status_to_string(status)); 117 | break; 118 | case WL_NO_SSID_AVAIL: 119 | Serial.print(F("Wifi Status: ")); 120 | Serial.println(wl_status_to_string(status)); 121 | Serial.println(F("Bad WiFi credentials")); 122 | return false; 123 | case WL_DISCONNECTED: 124 | Serial.print(F("Wifi Status: ")); 125 | Serial.println(wl_status_to_string(status)); 126 | Serial.println(F("Disconnected. (Check low RSSI)")); 127 | return false; 128 | default: 129 | Serial.print(F("Uncaught Status - Wifi Status: ")); 130 | Serial.println(wl_status_to_string(status)); 131 | break; 132 | } 133 | } 134 | delay(2000); // Giving time to connect 135 | connectionAttempts++; 136 | } 137 | 138 | 139 | #ifdef ARDUINO_ARCH_ESP32 140 | WiFi.setTxPower(WIFI_POWER_19_5dBm); // https://github.com/G6EJD/ESP32-8266-Adjust-WiFi-RF-Power-Output/blob/main/README.md 141 | #endif 142 | 143 | #ifdef ESP32 144 | WiFi.setTxPower(WIFI_POWER_19_5dBm); // https://github.com/G6EJD/ESP32-8266-Adjust-WiFi-RF-Power-Output/blob/main/README.md 145 | #endif 146 | Serial.print(F("IP_ADDRESS:")); // !!! Line required in this format for WifiSetup.html page to show IP Address correct. 147 | Serial.print(WiFi.localIP()); // !!! Line required in this format for WifiSetup.html page to show IP Address correct. 148 | Serial.println(F("\n ")); // !!! Line required in this format for WifiSetup.html page to show IP Address correct. 149 | 150 | Serial.print(F("Connected To Wifi Access Point: ")); 151 | Serial.println(globalVariables.SSID); 152 | Serial.print(F("Specific BSSID: ")); //MAC address of connected AP 153 | Serial.println(WiFi.BSSIDstr()); 154 | Serial.print(F("RSSI (Signal Strength): ")); 155 | Serial.println(WiFi.RSSI()); 156 | Serial.print(F("BLLED Controller IP Address: ")); 157 | Serial.println(WiFi.localIP()); 158 | Serial.println(); 159 | Serial.print(F("Use web browser to access 'http://")); // Instruction for user to go to Config page 160 | Serial.print(WiFi.localIP()); // Instruction for user to go to Config page 161 | Serial.println(F("/' to view the setup page")); // Instruction for user to go to Config page 162 | Serial.println(); 163 | return true; 164 | }; 165 | 166 | void startAPMode() { 167 | WiFi.disconnect(true, true); 168 | WiFi.mode(WIFI_AP); 169 | delay(500); 170 | WiFi.softAP("BLLED_AP"); 171 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 172 | dnsServer.start(53, "*", apIP); 173 | 174 | Serial.println(F("[WiFiManager] AP gestartet auf IP: ")); 175 | Serial.println(WiFi.softAPIP()); 176 | } 177 | 178 | void scanNetwork() 179 | { 180 | //Reference / Credit - https://www.esp32.com/viewtopic.php?t=18979#p70299 181 | int bestRSSI = -200; 182 | String bestBSSID = ""; 183 | 184 | Serial.println(F("Wifi network scan start")); 185 | int n = WiFi.scanNetworks(); 186 | Serial.println(F("Network scan complete")); 187 | 188 | if (n == 0) { 189 | Serial.println(F("No wifi networks found :(")); 190 | } else { 191 | Serial.print(n); // Count 192 | Serial.println(F(" Wifi networks found")); 193 | Serial.println(F("-------------------------------------------------------------------------")); 194 | for (int i = 0; i < n; ++i) { // Print SSID and RSSI for each network found 195 | Serial.print(i + 1); 196 | Serial.print(F(": ")); 197 | Serial.print(WiFi.SSID(i)); 198 | Serial.print(F(" (")); 199 | Serial.print(WiFi.RSSI(i)); 200 | Serial.print(F("dBm, ")); 201 | if (WiFi.RSSI(i) < -50) Serial.print(F(" ")); //Spacing... because I just can't look at the results when misaligned. 202 | Serial.print(constrain(2 * (WiFi.RSSI(i) + 100), 0, 100)); 203 | Serial.print(F("%) ")); 204 | Serial.print(F("BSSID: ")); 205 | Serial.print(WiFi.BSSIDstr(i)); 206 | Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " open" : " encrypted"); 207 | 208 | if(WiFi.SSID(i) == globalVariables.SSID && WiFi.RSSI(i) > bestRSSI) 209 | { 210 | bestBSSID = WiFi.BSSIDstr(i); 211 | bestRSSI = WiFi.RSSI(i); 212 | } 213 | delay(10); 214 | } 215 | Serial.println(); 216 | if(printerConfig.BSSID == bestBSSID.c_str()){ 217 | Serial.print(F("BSSID already set to: ")); 218 | Serial.print(printerConfig.BSSID); 219 | } 220 | else{ 221 | if(strlen(printerConfig.BSSID) == 0 || printerConfig.rescanWiFiNetwork){ 222 | strcpy(printerConfig.BSSID,bestBSSID.c_str()); 223 | Serial.print(F("Saving strongest AP for: ")); 224 | Serial.println(globalVariables.SSID); 225 | Serial.print(F("Strongest BSSID (MAC Address): ")); 226 | Serial.print(printerConfig.BSSID); 227 | } 228 | else{ 229 | Serial.print(F("Ignoring strongest AP for: ")); 230 | Serial.println(globalVariables.SSID); 231 | Serial.print(F("Using alrady saved BSSID (MAC Address): ")); 232 | Serial.print(printerConfig.BSSID); 233 | } 234 | Serial.print(F(" RSSI (Signal Strength): ")); 235 | Serial.print(bestRSSI); 236 | Serial.println(F("dBm")); 237 | } 238 | } 239 | Serial.println(); 240 | } 241 | 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | bool shouldRestart = false; 3 | unsigned long restartRequestTime = 0; 4 | #include "./blled/logSerial.h" 5 | #include "./blled/web-server.h" 6 | #include "./blled/mqttmanager.h" 7 | #include "./blled/filesystem.h" 8 | #include "./blled/types.h" 9 | #include "./blled/leds.h" 10 | #include "./blled/serialmanager.h" 11 | #include "./blled/wifi-manager.h" 12 | #include "./blled/ssdp.h" 13 | 14 | int wifi_reconnect_count = 0; 15 | 16 | void defaultcolors() 17 | { 18 | LogSerial.println(F("Setting default customisable colors")); 19 | printerConfig.runningColor = hex2rgb("#000000", 255, 255); // WHITE Running 20 | printerConfig.testColor = hex2rgb("#3F3CFB"); // Violet Test 21 | printerConfig.finishColor = hex2rgb("#00FF00"); // Green Finish 22 | 23 | printerConfig.stage14Color = hex2rgb("#000000"); // OFF Cleaning Nozzle 24 | printerConfig.stage1Color = hex2rgb("#000055"); // OFF Bed Leveling 25 | printerConfig.stage8Color = hex2rgb("#000000"); // OFF Calibrating Extrusion 26 | printerConfig.stage9Color = hex2rgb("#000000"); // OFF Scanning Bed Surface 27 | printerConfig.stage10Color = hex2rgb("#000000"); // OFF First Layer Inspection 28 | 29 | printerConfig.wifiRGB = hex2rgb("#FFA500"); // Orange Wifi Scan 30 | 31 | printerConfig.pauseRGB = hex2rgb("#0000FF"); // Blue Pause 32 | printerConfig.firstlayerRGB = hex2rgb("#0000FF"); // Blue 33 | printerConfig.nozzleclogRGB = hex2rgb("#0000FF"); // Blue 34 | printerConfig.hmsSeriousRGB = hex2rgb("#FF0000"); // Red 35 | printerConfig.hmsFatalRGB = hex2rgb("#FF0000"); // Red 36 | printerConfig.filamentRunoutRGB = hex2rgb("#FF0000"); // Red 37 | printerConfig.frontCoverRGB = hex2rgb("#FF0000"); // Red 38 | printerConfig.nozzleTempRGB = hex2rgb("#FF0000"); // Red 39 | printerConfig.bedTempRGB = hex2rgb("#FF0000"); // Red 40 | } 41 | 42 | void setup() 43 | { 44 | Serial.begin(115200); 45 | delay(100); 46 | Serial.println(F("Initializing")); 47 | Serial.println(ESP.getFreeHeap()); 48 | Serial.println(""); 49 | Serial.print(F("** Using firmware version: ")); 50 | Serial.print(globalVariables.FWVersion); 51 | Serial.println(F(" **")); 52 | Serial.println(""); 53 | defaultcolors(); 54 | setupLeds(); 55 | tweenToColor(100, 100, 100, 100, 100); // ALL LEDS ON 56 | Serial.println(F("")); 57 | 58 | tweenToColor(255, 0, 0, 0, 0); // RED 59 | setupFileSystem(); 60 | loadFileSystem(); 61 | Serial.println(F("")); 62 | 63 | tweenToColor(printerConfig.wifiRGB); // Customisable - Default is ORANGE 64 | setupSerial(); 65 | 66 | if (strlen(globalVariables.SSID) == 0 || strlen(globalVariables.APPW) == 0) 67 | { 68 | Serial.println(F("SSID or password is missing. Please configure both by going to: https://dutchdevelop.com/blled-configuration-setup/")); 69 | tweenToColor(100, 0, 100, 0, 0); // PINK 70 | startAPMode(); 71 | setupWebserver(); 72 | return; 73 | } 74 | else 75 | 76 | if (!connectToWifi()) 77 | { 78 | Serial.println(F("[WiFiManager] Not connected → AP Mode")); 79 | startAPMode(); 80 | setupWebserver(); 81 | return; 82 | } 83 | else 84 | { 85 | Serial.println(F("[WiFiManager] connected. Starting webUI.")); 86 | tweenToColor(0, 0, 255, 0, 0); // BLUE 87 | setupWebserver(); 88 | } 89 | 90 | start_ssdp(); 91 | 92 | tweenToColor(34, 224, 238, 0, 0); // CYAN 93 | setupMqtt(); 94 | 95 | Serial.println(); 96 | Serial.print(F("** BLLED Controller started ")); 97 | Serial.print(F("using firmware version: ")); 98 | Serial.print(globalVariables.FWVersion); 99 | Serial.println(F(" **")); 100 | Serial.println(); 101 | globalVariables.started = true; 102 | Serial.println(F("Updating LEDs from Setup")); 103 | updateleds(); 104 | } 105 | 106 | void loop() 107 | { 108 | serialLoop(); 109 | if (globalVariables.started) 110 | { 111 | websocketLoop(); 112 | ledsloop(); 113 | 114 | if (WiFi.status() != WL_CONNECTED) 115 | { 116 | LogSerial.print(F("Wifi connection dropped. ")); 117 | LogSerial.print(F("Wifi Status: ")); 118 | LogSerial.println(wl_status_to_string(WiFi.status())); 119 | LogSerial.println(F("Attempting to reconnect to WiFi...")); 120 | wifi_reconnect_count += 1; 121 | if (wifi_reconnect_count <= 2) 122 | { 123 | WiFi.disconnect(); 124 | delay(100); 125 | WiFi.reconnect(); 126 | } 127 | else 128 | { 129 | // Not connecting after 10 simple disconnect / reconnects 130 | // Do something more drastic in case needing to switch to new AP 131 | scanNetwork(); 132 | connectToWifi(); 133 | wifi_reconnect_count = 0; 134 | } 135 | } 136 | if (WiFi.getMode() == WIFI_AP) 137 | { 138 | dnsServer.processNextRequest(); 139 | } 140 | } 141 | if (printerConfig.rescanWiFiNetwork) 142 | { 143 | LogSerial.println(F("Web submitted refresh of Wifi Scan (assigning Strongest AP)")); 144 | tweenToColor(printerConfig.wifiRGB); // Customisable - Default is ORANGE 145 | scanNetwork(); // Sets the MAC address for following connection attempt 146 | printerConfig.rescanWiFiNetwork = false; 147 | updateleds(); 148 | } 149 | if (shouldRestart && millis() - restartRequestTime > 1500) 150 | { 151 | LogSerial.println(F("[WiFiSetup] Restarting now...")); 152 | ESP.restart(); 153 | } 154 | } -------------------------------------------------------------------------------- /src/www/backupRestore.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BLLED Config Backup 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 | 33 |

34 |
35 |
36 | 37 | 38 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/www/blled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 43 | -------------------------------------------------------------------------------- /src/www/colorpicker.html.unused: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BLLED Custom Colorpicker 6 | 64 | 65 | 66 | 67 |

LED Farbe wählen

68 |
69 | 70 |
71 | 85 | 86 |

 87 | 
 88 | 
145 | 
146 | 
147 | 
148 | 


--------------------------------------------------------------------------------
/src/www/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DutchDevelop/BLLEDController/455278c13bc365c75accfc459a9c45db41c211c4/src/www/favicon.png


--------------------------------------------------------------------------------
/src/www/particleCanvas.js:
--------------------------------------------------------------------------------
 1 |         const canvas = document.getElementById('particleCanvas');
 2 |         const ctx = canvas.getContext('2d');
 3 |         canvas.width = window.innerWidth;
 4 |         canvas.height = window.innerHeight;
 5 | 
 6 |         let particlesArray = [];
 7 |         const numberOfParticles = 100;
 8 | 
 9 |         class Particle {
10 |             constructor(x, y, directionX, directionY, size, color) {
11 |                 this.x = x;
12 |                 this.y = y;
13 |                 this.directionX = directionX;
14 |                 this.directionY = directionY;
15 |                 this.size = size;
16 |                 this.color = color;
17 |             }
18 |             draw() {
19 |                 ctx.beginPath();
20 |                 ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
21 |                 ctx.fillStyle = this.color;
22 |                 ctx.fill();
23 |             }
24 | 
25 | 
26 |             update() {
27 |                 if (this.x + this.size > canvas.width || this.x - this.size < 0) {
28 |                     this.directionX = -this.directionX;
29 |                 }
30 | 
31 |                 if (this.y + this.size > canvas.height || this.y - this.size < 0) {
32 |                     this.directionY = -this.directionY;
33 |                 }
34 | 
35 |                 this.x += this.directionX;
36 |                 this.y += this.directionY;
37 | 
38 |                 this.draw();
39 |             }
40 |         }
41 | 
42 |         function init() {
43 |             particlesArray = [];
44 |             for (let i = 0; i < numberOfParticles; i++) {
45 |                 let size = Math.random() * 5 + 1;
46 |                 let x = Math.random() * (canvas.width - size * 2) + size;
47 |                 let y = Math.random() * (canvas.height - size * 2) + size;
48 |                 let directionX = (Math.random() * 0.5) - 0.25;
49 |                 let directionY = (Math.random() * 0.5) - 0.25;
50 |                 let color = 'rgba(255, 255, 255, 0.8)';
51 | 
52 |                 particlesArray.push(new Particle(x, y, directionX, directionY, size, color));
53 |             }
54 |         }
55 | 
56 |         function connect() {
57 |             let opacityValue = 1;
58 |             for (let a = 0; a < particlesArray.length; a++) {
59 |                 for (let b = a; b < particlesArray.length; b++) {
60 |                     let distance = ((particlesArray[a].x - particlesArray[b].x) * (particlesArray[a].x - particlesArray[b].x)) 
61 |                                  + ((particlesArray[a].y - particlesArray[b].y) * (particlesArray[a].y - particlesArray[b].y));
62 | 
63 |                     if (distance < (canvas.width / 7) * (canvas.height / 7)) {
64 |                         opacityValue = 1 - (distance / 20000);
65 |                         ctx.strokeStyle = 'rgba(255, 255, 255,' + opacityValue + ')';
66 |                         ctx.lineWidth = 1;
67 |                         ctx.beginPath();
68 |                         ctx.moveTo(particlesArray[a].x, particlesArray[a].y);
69 |                         ctx.lineTo(particlesArray[b].x, particlesArray[b].y);
70 |                         ctx.stroke();
71 |                     }
72 |                 }
73 |             }
74 |         }
75 | 
76 |         function animate() {
77 |             ctx.clearRect(0, 0, canvas.width, canvas.height);
78 | 
79 |             for (let i = 0; i < particlesArray.length; i++) {
80 |                 particlesArray[i].update();
81 |             }
82 | 
83 |             connect();
84 |             requestAnimationFrame(animate);
85 |         }
86 | 
87 |         window.addEventListener('resize', () => {
88 |             canvas.width = window.innerWidth;
89 |             canvas.height = window.innerHeight;
90 |             init();
91 |         });
92 | 
93 |         init();
94 |         animate();


--------------------------------------------------------------------------------
/src/www/style.css:
--------------------------------------------------------------------------------
  1 | body {
  2 |   font-family: Arial, sans-serif;
  3 |   background: #f9f9f9;
  4 |   color: #333;
  5 |   margin: 0;
  6 |   padding: 15px;
  7 |   display: flex;
  8 |   flex-direction: column;
  9 |   align-items: center;
 10 | }
 11 | 
 12 | canvas {
 13 |   position: fixed;
 14 |   top: 0;
 15 |   left: 0;
 16 |   width: 100vw;
 17 |   height: 100vh;
 18 |   z-index: -1;
 19 |   background: rgb(20, 80, 49);
 20 | }
 21 | 
 22 | #container {
 23 |   max-width: 450px;
 24 |   width: 100%;
 25 |   z-index: 1;
 26 |   padding: 20px;
 27 | }
 28 | 
 29 | #header {
 30 |   display: flex;
 31 |   align-items: center;
 32 |   justify-content: center;
 33 |   gap: 8px;
 34 |   margin-bottom: 20px;
 35 | }
 36 | 
 37 | #logo {
 38 |   height: 5em;
 39 |   width: auto;
 40 |   display: inline-block;
 41 | }
 42 | 
 43 | svg#wifi-symbol {
 44 |   width: 60px;
 45 |   height: 55px;
 46 |   max-width: 100%;
 47 |   padding-right: 20px;
 48 | }
 49 | 
 50 | svg#mqtt_status_svg {
 51 |   width: 100px;
 52 |   height: auto;
 53 |   max-width: 100%;
 54 |   padding-left: 20px;
 55 | }
 56 | 
 57 | h1,
 58 | h2 {
 59 |   color: white;
 60 |   text-align: center;
 61 | }
 62 | 
 63 | h4 {
 64 |   color: white;
 65 |   text-align: center;
 66 |   margin: 5px;
 67 | }
 68 | 
 69 | .form-container {
 70 |   background-color: #fff;
 71 |   padding: 0px;
 72 |   border-radius: 5px;
 73 |   box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
 74 |   color: #333;
 75 | }
 76 | 
 77 | label {
 78 |   display: block;
 79 |   font-weight: bold;
 80 |   margin-bottom: 5px;
 81 | }
 82 | 
 83 | input[type="text"],
 84 | input[type="password"],
 85 | input[type="file"],
 86 | select {
 87 |   width: 100%;
 88 |   padding: 6px;
 89 |   margin-bottom: 8px;
 90 |   box-sizing: border-box;
 91 |   border: 1px solid #ccc;
 92 |   border-radius: 3px;
 93 | }
 94 | 
 95 | input[type="range"] {
 96 |   width: 100%;
 97 |   margin-bottom: 10px;
 98 | }
 99 | 
100 | input[type="color"] {
101 |   width: 30px;
102 |   height: 30px;
103 |   padding: 0;
104 |   background: none;
105 |   border: 1px solid #ccc;
106 |   border-radius: 3px;
107 |   cursor: pointer;
108 | }
109 | 
110 | .input-inline-group {
111 |   display: flex;
112 |   align-items: center;
113 |   flex-wrap: wrap;
114 |   padding: 2px;
115 |   border: 0;
116 |   gap: 0px;
117 | }
118 | 
119 | .input-inline-group input[type="number"] {
120 |   text-align: center;
121 |   height: 20px;
122 |   width: 40px;
123 |   border: 1px solid #ccc;
124 |   border-radius: 3px;
125 | }
126 | 
127 | .input-inline-group label {
128 |   margin: 0;
129 |   font-weight: normal;
130 |   border: 0;
131 | }
132 | 
133 | button {
134 |   background-color: #484;
135 |   border: none;
136 |   color: white;
137 |   padding: 8px 16px;
138 |   font-size: 14px;
139 |   border-radius: 3px;
140 |   cursor: pointer;
141 |   width: 100%;
142 |   margin-bottom: 10px;
143 | }
144 | 
145 | .downloadConfigButton {
146 |   padding: 14px;
147 | }
148 | 
149 | button:hover {
150 |   background-color: #4CAF50;
151 | }
152 | 
153 | button:disabled {
154 |   background-color: #999;
155 |   cursor: not-allowed;
156 | }
157 | 
158 | .toggle-switch {
159 |   display: flex;
160 |   align-items: center;
161 |   gap: 10px;
162 |   margin-bottom: 10px;
163 | }
164 | 
165 | .switch {
166 |   position: relative;
167 |   display: inline-block;
168 |   width: 50px;
169 |   height: 24px;
170 | }
171 | 
172 | .switch input {
173 |   opacity: 0;
174 |   width: 0;
175 |   height: 0;
176 | }
177 | 
178 | .slider {
179 |   position: absolute;
180 |   cursor: pointer;
181 |   top: 0;
182 |   left: 0;
183 |   right: 0;
184 |   bottom: 0;
185 |   background-color: #ccc;
186 |   transition: .4s;
187 |   border-radius: 24px;
188 | }
189 | 
190 | .slider:before {
191 |   position: absolute;
192 |   content: "";
193 |   height: 18px;
194 |   width: 18px;
195 |   left: 3px;
196 |   bottom: 3px;
197 |   background-color: white;
198 |   transition: .4s;
199 |   border-radius: 50%;
200 | }
201 | 
202 | input:checked+.slider {
203 |   background-color: #28a745;
204 | }
205 | 
206 | input:checked+.slider:before {
207 |   transform: translateX(26px);
208 | }
209 | 
210 | #toast {
211 |   position: fixed;
212 |   top: 50%;
213 |   left: 50%;
214 |   transform: translate(-50%, -50%) scale(0.9);
215 |   background-color: #333;
216 |   color: white;
217 |   padding: 14px 24px;
218 |   border-radius: 6px;
219 |   font-size: 15px;
220 |   display: flex;
221 |   align-items: center;
222 |   gap: 10px;
223 |   z-index: 9999;
224 |   opacity: 0;
225 |   transition: opacity 0.4s ease, transform 0.3s ease;
226 |   pointer-events: none;
227 |   box-shadow: 0 0 12px rgba(0, 0, 0, 0.3);
228 | }
229 | 
230 | #toast.show {
231 |   opacity: 1;
232 |   transform: translate(-50%, -50%) scale(1);
233 | }
234 | 
235 | #toast-icon {
236 |   font-size: 18px;
237 | }
238 | 
239 | progress {
240 |   width: 100%;
241 |   height: 15px;
242 |   margin-bottom: 10px;
243 |   border-radius: 5px;
244 |   overflow: hidden;
245 | }
246 | 
247 | #status {
248 |   font-weight: bold;
249 |   text-align: center;
250 |   margin-top: 10px;
251 |   color: #4CAF50;
252 | }
253 | 
254 | .collapse {
255 |   margin-bottom: 10px;
256 |   margin-top: 10px;
257 | }
258 | 
259 | .collapse summary {
260 |   font-weight: bold;
261 |   cursor: pointer;
262 |   background-color: #e0e0e0;
263 |   padding: 10px;
264 |   border-radius: 4px;
265 | }
266 | 
267 | .collapse[open] summary {
268 |   background-color: #ccc;
269 | }
270 | 
271 | .collapse div {
272 |   padding: 0px;
273 |   border: 0px solid #ccc;
274 |   border-top: none;
275 | }
276 | 
277 | details.collapse>div {
278 |   max-height: 0;
279 |   overflow: hidden;
280 |   transition: max-height 0.4s ease, opacity 0.4s ease;
281 |   opacity: 0;
282 |   padding-bottom: 0;
283 | }
284 | 
285 | details.collapse[open]>div {
286 |   max-height: unset;
287 |   opacity: 1;
288 |   padding-bottom: 10px;
289 | }
290 | 
291 | .detailSplitter {
292 |   background-color: #ccc;
293 |   font-weight: bold;
294 |   text-align: center;
295 |   padding: 10px;
296 |   margin-bottom: 8px;
297 |   border-radius: 4px;
298 | }
299 | 
300 | .network {
301 |   background: #fff;
302 |   border: 1px solid #ddd;
303 |   border-radius: 4px;
304 |   padding: 4px 8px;
305 |   display: flex;
306 |   align-items: center;
307 |   gap: 8px;
308 |   font-size: 14px;
309 |   cursor: pointer;
310 | }
311 | 
312 | .network:hover {
313 |   background: #eee;
314 | }
315 | 
316 | form {
317 |   margin-top: 15px;
318 |   background: #fff;
319 |   padding: 10px;
320 |   border-radius: 4px;
321 |   font-size: 14px;
322 | }
323 | 
324 | .input-inline-group input[type="text"] {
325 |   width: 50px;
326 |   display: inline-block;
327 | }


--------------------------------------------------------------------------------
/src/www/updatePage.html:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 | 
  5 |     
  6 |     
  7 |     BLLED OTA Update
  8 |     
  9 | 
 10 | 
 11 | 
 12 |     
 13 | 
 14 |     
15 | 19 | 20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |

Status: Ready

32 |
33 |
34 | 35 | 36 | 147 |
ℹ️Placeholder
148 | 149 | 150 | -------------------------------------------------------------------------------- /src/www/webSerialPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Web Console 8 | 9 | 190 | 191 | 192 | 193 | 194 |
195 | 199 | 200 |
201 |
202 | 210 | 218 | 229 |
230 | 231 | 237 |
238 |

239 |

240 | 241 | 242 | 393 | 394 | -------------------------------------------------------------------------------- /src/www/wifiSetup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BLLED WiFi Setup 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 | 19 |
Loading...
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 |
47 |
48 |
49 | 50 | 51 |
ℹ️Placeholder
52 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Test Runner and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html 12 | -------------------------------------------------------------------------------- /test/printerjsonexample.json: -------------------------------------------------------------------------------- 1 | { 2 | "2D": { 3 | "bs": { 4 | "bi": [ 5 | { 6 | "clock_in": false, 7 | "est_time": 0, 8 | "idx": 0, 9 | "print_then": false, 10 | "proc_list": [], 11 | "step_type": 1, 12 | "tool_info": { 13 | "color": "", 14 | "diameter": 0.4000000059604645, 15 | "id": 8978432 16 | }, 17 | "type": 137 18 | } 19 | ], 20 | "total_time": 0 21 | }, 22 | "cond": 14, 23 | "cur_stage": { 24 | "clock_in_time": 0, 25 | "idx": 0, 26 | "left_time": 0, 27 | "process": 0, 28 | "state": 0 29 | }, 30 | "first_confirm": false, 31 | "makeable": false, 32 | "material": { 33 | "cur_id_list": [], 34 | "state": 0, 35 | "tar_id": "", 36 | "tar_name": "" 37 | } 38 | }, 39 | "3D": { 40 | "layer_num": 1, 41 | "total_layer_num": 435 42 | }, 43 | "ams": { 44 | "ams": [ 45 | { 46 | "dry_time": 0, 47 | "humidity": "4", 48 | "humidity_raw": "25", 49 | "id": "0", 50 | "info": "1003", 51 | "temp": "27.5", 52 | "tray": [ 53 | { 54 | "bed_temp": "0", 55 | "bed_temp_type": "0", 56 | "cali_idx": -1, 57 | "cols": [ 58 | "000000FF" 59 | ], 60 | "ctype": 0, 61 | "drying_temp": "80", 62 | "drying_time": "8", 63 | "id": "0", 64 | "nozzle_temp_max": "270", 65 | "nozzle_temp_min": "240", 66 | "remain": 100, 67 | "state": 11, 68 | "tag_uid": "CAD4127600000100", 69 | "total_len": 330000, 70 | "tray_color": "000000FF", 71 | "tray_diameter": "1.75", 72 | "tray_id_name": "B00-K0", 73 | "tray_info_idx": "GFB00", 74 | "tray_sub_brands": "ABS", 75 | "tray_type": "ABS", 76 | "tray_uuid": "4CD95CB1ABC44B0EA1EDA631CB0595A8", 77 | "tray_weight": "1000", 78 | "xcam_info": "803E1027E803E8033333333F" 79 | }, 80 | { 81 | "bed_temp": "0", 82 | "bed_temp_type": "0", 83 | "cali_idx": -1, 84 | "cols": [ 85 | "000000FF" 86 | ], 87 | "ctype": 0, 88 | "drying_temp": "80", 89 | "drying_time": "8", 90 | "id": "1", 91 | "nozzle_temp_max": "270", 92 | "nozzle_temp_min": "240", 93 | "remain": 100, 94 | "state": 11, 95 | "tag_uid": "FA261DF400000100", 96 | "total_len": 330000, 97 | "tray_color": "000000FF", 98 | "tray_diameter": "1.75", 99 | "tray_id_name": "B00-K0", 100 | "tray_info_idx": "GFB00", 101 | "tray_sub_brands": "ABS", 102 | "tray_type": "ABS", 103 | "tray_uuid": "8A892CB7DF894F68A08C0A8E28DCFDF1", 104 | "tray_weight": "1000", 105 | "xcam_info": "803E1027E803E8033333333F" 106 | }, 107 | { 108 | "bed_temp": "70", 109 | "bed_temp_type": "2", 110 | "cali_idx": -1, 111 | "cols": [ 112 | "F2EADAFF" 113 | ], 114 | "ctype": 0, 115 | "drying_temp": "65", 116 | "drying_time": "8", 117 | "id": "2", 118 | "nozzle_temp_max": "270", 119 | "nozzle_temp_min": "220", 120 | "remain": 100, 121 | "state": 11, 122 | "tag_uid": "324BBF7400000100", 123 | "total_len": 330000, 124 | "tray_color": "F2EADAFF", 125 | "tray_diameter": "1.75", 126 | "tray_id_name": "G00-W4", 127 | "tray_info_idx": "GFG00", 128 | "tray_sub_brands": "PETG Basic", 129 | "tray_type": "PETG", 130 | "tray_uuid": "E3604E1558A54D2B8F65FB017E85D649", 131 | "tray_weight": "1000", 132 | "xcam_info": "8813D007E803E8039A99193F" 133 | }, 134 | { 135 | "bed_temp": "80", 136 | "bed_temp_type": "1", 137 | "cali_idx": -1, 138 | "cols": [ 139 | "000000FF" 140 | ], 141 | "ctype": 0, 142 | "drying_temp": "80", 143 | "drying_time": "8", 144 | "id": "3", 145 | "nozzle_temp_max": "270", 146 | "nozzle_temp_min": "240", 147 | "remain": 53, 148 | "state": 11, 149 | "tag_uid": "60347D2F00000100", 150 | "total_len": 330000, 151 | "tray_color": "000000FF", 152 | "tray_diameter": "1.75", 153 | "tray_id_name": "B01-K0", 154 | "tray_info_idx": "GFB01", 155 | "tray_sub_brands": "ASA", 156 | "tray_type": "ASA", 157 | "tray_uuid": "E746641127C74A8A8276FB4A6A1D9B97", 158 | "tray_weight": "1000", 159 | "xcam_info": "803E1027E803E8030000803F" 160 | } 161 | ] 162 | }, 163 | { 164 | "dry_time": 0, 165 | "humidity": "4", 166 | "humidity_raw": "25", 167 | "id": "1", 168 | "info": "1103", 169 | "temp": "27.7", 170 | "tray": [ 171 | { 172 | "bed_temp": "90", 173 | "bed_temp_type": "2", 174 | "cali_idx": -1, 175 | "cols": [ 176 | "FFFFFFFF" 177 | ], 178 | "ctype": 0, 179 | "drying_temp": "80", 180 | "drying_time": "8", 181 | "id": "0", 182 | "nozzle_temp_max": "270", 183 | "nozzle_temp_min": "240", 184 | "remain": 60, 185 | "state": 11, 186 | "tag_uid": "70F7792E00000100", 187 | "total_len": 330000, 188 | "tray_color": "FFFFFFFF", 189 | "tray_diameter": "1.75", 190 | "tray_id_name": "B00-W0", 191 | "tray_info_idx": "GFB00", 192 | "tray_sub_brands": "ABS", 193 | "tray_type": "ABS", 194 | "tray_uuid": "DE09C5C1E84A41F8981AA97FC30187CE", 195 | "tray_weight": "1000", 196 | "xcam_info": "D007D007E803E803CDCC4C3F" 197 | }, 198 | { 199 | "bed_temp": "35", 200 | "bed_temp_type": "1", 201 | "cali_idx": -1, 202 | "cols": [ 203 | "000000FF" 204 | ], 205 | "ctype": 0, 206 | "drying_temp": "55", 207 | "drying_time": "8", 208 | "id": "1", 209 | "nozzle_temp_max": "230", 210 | "nozzle_temp_min": "190", 211 | "remain": 100, 212 | "state": 11, 213 | "tag_uid": "6CE9285500000100", 214 | "total_len": 330000, 215 | "tray_color": "000000FF", 216 | "tray_diameter": "1.75", 217 | "tray_id_name": "A00-K0", 218 | "tray_info_idx": "GFA00", 219 | "tray_sub_brands": "PLA Basic", 220 | "tray_type": "PLA", 221 | "tray_uuid": "F849A72301624376B132C2FF96A5C498", 222 | "tray_weight": "1000", 223 | "xcam_info": "803E803EE803E803CDCC4C3F" 224 | }, 225 | { 226 | "bed_temp": "35", 227 | "bed_temp_type": "1", 228 | "cali_idx": -1, 229 | "cols": [ 230 | "F17B8FFF" 231 | ], 232 | "ctype": 0, 233 | "drying_temp": "55", 234 | "drying_time": "8", 235 | "id": "2", 236 | "nozzle_temp_max": "230", 237 | "nozzle_temp_min": "190", 238 | "remain": 100, 239 | "state": 11, 240 | "tag_uid": "BC2C04D000000100", 241 | "total_len": 330000, 242 | "tray_color": "F17B8FFF", 243 | "tray_diameter": "1.75", 244 | "tray_id_name": "A12-R0", 245 | "tray_info_idx": "GFA12", 246 | "tray_sub_brands": "PLA Glow", 247 | "tray_type": "PLA", 248 | "tray_uuid": "DEF3509E89094379A419EADEF3BE9F45", 249 | "tray_weight": "1000", 250 | "xcam_info": "AC0DD0078403E8033333333F" 251 | }, 252 | { 253 | "bed_temp": "35", 254 | "bed_temp_type": "1", 255 | "cali_idx": -1, 256 | "cols": [ 257 | "A1FFACFF" 258 | ], 259 | "ctype": 0, 260 | "drying_temp": "55", 261 | "drying_time": "8", 262 | "id": "3", 263 | "nozzle_temp_max": "230", 264 | "nozzle_temp_min": "190", 265 | "remain": 100, 266 | "state": 11, 267 | "tag_uid": "AA0A93AA00000100", 268 | "total_len": 330000, 269 | "tray_color": "A1FFACFF", 270 | "tray_diameter": "1.75", 271 | "tray_id_name": "A12-G0", 272 | "tray_info_idx": "GFA12", 273 | "tray_sub_brands": "PLA Glow", 274 | "tray_type": "PLA", 275 | "tray_uuid": "17A15669F8524BAEA8A0F5CA89E609D2", 276 | "tray_weight": "1000", 277 | "xcam_info": "881388138403E8033333333F" 278 | } 279 | ] 280 | } 281 | ], 282 | "ams_exist_bits": "3", 283 | "ams_exist_bits_raw": "3", 284 | "cali_id": 255, 285 | "cali_stat": 0, 286 | "insert_flag": true, 287 | "power_on_flag": false, 288 | "tray_exist_bits": "ff", 289 | "tray_is_bbl_bits": "ff", 290 | "tray_now": "1", 291 | "tray_pre": "1", 292 | "tray_read_done_bits": "ff", 293 | "tray_reading_bits": "0", 294 | "tray_tar": "1", 295 | "unbind_ams_stat": 0, 296 | "version": 69132 297 | }, 298 | "ams_rfid_status": 0, 299 | "ams_status": 768, 300 | "ap_err": 0, 301 | "aux": "2001004", 302 | "aux_part_fan": false, 303 | "batch_id": 0, 304 | "bed_target_temper": 55, 305 | "bed_temper": 55, 306 | "big_fan1_speed": "0", 307 | "big_fan2_speed": "4", 308 | "cali_version": 0, 309 | "canvas_id": 0, 310 | "care": [ 311 | { 312 | "id": "lr", 313 | "info": "1864" 314 | }, 315 | { 316 | "id": "fa", 317 | "info": "1864" 318 | }, 319 | { 320 | "id": "ls", 321 | "info": "1864" 322 | }, 323 | { 324 | "id": "cr", 325 | "info": "1864" 326 | }, 327 | { 328 | "id": "ld", 329 | "info": "1864" 330 | } 331 | ], 332 | "cfg": "385FDAD9", 333 | "chamber_temper": 32, 334 | "command": "push_status", 335 | "cooling_fan_speed": "0", 336 | "ctt": 0, 337 | "design_id": "", 338 | "device": { 339 | "airduct": { 340 | "modeCur": 0, 341 | "modeList": [ 342 | { 343 | "ctrl": [ 344 | 16, 345 | 32, 346 | 48 347 | ], 348 | "modeId": 0, 349 | "off": [ 350 | 96 351 | ] 352 | }, 353 | { 354 | "ctrl": [ 355 | 16 356 | ], 357 | "modeId": 1, 358 | "off": [ 359 | 32, 360 | 48 361 | ] 362 | }, 363 | { 364 | "ctrl": [ 365 | 48 366 | ], 367 | "modeId": 2, 368 | "off": [] 369 | } 370 | ], 371 | "parts": [ 372 | { 373 | "func": 0, 374 | "id": 16, 375 | "range": 6553600, 376 | "state": 0 377 | }, 378 | { 379 | "func": 1, 380 | "id": 32, 381 | "range": 6553600, 382 | "state": 0 383 | }, 384 | { 385 | "func": 2, 386 | "id": 48, 387 | "range": 6553600, 388 | "state": 30 389 | }, 390 | { 391 | "func": 3, 392 | "id": 96, 393 | "range": 6553600, 394 | "state": 0 395 | } 396 | ] 397 | }, 398 | "bed": { 399 | "info": { 400 | "temp": 3604535 401 | }, 402 | "state": 2 403 | }, 404 | "bed_temp": 3604535, 405 | "cam": { 406 | "laser": { 407 | "cond": 252, 408 | "state": 0 409 | } 410 | }, 411 | "cham_temp": 32, 412 | "ctc": { 413 | "info": { 414 | "temp": 31 415 | }, 416 | "state": 0 417 | }, 418 | "ext_tool": { 419 | "calib": 2, 420 | "low_prec": true, 421 | "mount": 0, 422 | "th_temp": 0, 423 | "type": "" 424 | }, 425 | "extruder": { 426 | "info": [ 427 | { 428 | "filam_bak": [ 429 | 3 430 | ], 431 | "hnow": 0, 432 | "hpre": 0, 433 | "htar": 0, 434 | "id": 0, 435 | "info": 9, 436 | "snow": 65535, 437 | "spre": 65535, 438 | "star": 65535, 439 | "stat": 0, 440 | "temp": 44 441 | }, 442 | { 443 | "filam_bak": [], 444 | "hnow": 1, 445 | "hpre": 1, 446 | "htar": 1, 447 | "id": 1, 448 | "info": 94, 449 | "snow": 257, 450 | "spre": 257, 451 | "star": 257, 452 | "stat": 197376, 453 | "temp": 14418140 454 | } 455 | ], 456 | "state": 33042 457 | }, 458 | "fan": 410080, 459 | "laser": { 460 | "power": 0 461 | }, 462 | "nozzle": { 463 | "exist": 3, 464 | "info": [ 465 | { 466 | "diameter": 0.4, 467 | "id": 0, 468 | "tm": 0, 469 | "type": "HS01", 470 | "wear": 0 471 | }, 472 | { 473 | "diameter": 0.4, 474 | "id": 1, 475 | "tm": 0, 476 | "type": "HS01", 477 | "wear": 0 478 | } 479 | ], 480 | "state": 0 481 | }, 482 | "plate": { 483 | "base": 4, 484 | "cali2d_id": "", 485 | "cur_id": "P0101", 486 | "mat": 1, 487 | "tar_id": "" 488 | }, 489 | "type": 1 490 | }, 491 | "err": "0", 492 | "fail_reason": "0", 493 | "fan_gear": 4980736, 494 | "file": "/data/Metadata/plate_1.gcode", 495 | "force_upgrade": false, 496 | "fun": "41AFFF9CFF", 497 | "gcode_file": "/data/Metadata/plate_1.gcode", 498 | "gcode_file_prepare_percent": "100", 499 | "gcode_state": "RUNNING", 500 | "heatbreak_fan_speed": "15", 501 | "hms": [], 502 | "home_flag": -1066934785, 503 | "hw_switch_state": 2, 504 | "ipcam": { 505 | "agora_service": "disable", 506 | "brtc_service": "enable", 507 | "bs_state": 0, 508 | "ipcam_dev": "1", 509 | "ipcam_record": "enable", 510 | "laser_preview_res": 5, 511 | "mode_bits": 2, 512 | "resolution": "1080p", 513 | "rtsp_url": "disable", 514 | "timelapse": "disable", 515 | "tl_store_hpd_type": 2, 516 | "tl_store_path_type": 2, 517 | "tutk_server": "enable" 518 | }, 519 | "job": { 520 | "cur_stage": { 521 | "idx": 0, 522 | "state": 2 523 | }, 524 | "stage": [ 525 | { 526 | "clock_in": false, 527 | "color": [ 528 | "", 529 | "" 530 | ], 531 | "diameter": [ 532 | 0.4000000059604645, 533 | 0.4000000059604645 534 | ], 535 | "est_time": 0, 536 | "heigh": 0, 537 | "idx": 0, 538 | "platform": "", 539 | "print_then": false, 540 | "proc_list": [], 541 | "tool": [ 542 | "HS01", 543 | "HS01" 544 | ], 545 | "type": 2 546 | } 547 | ] 548 | }, 549 | "job_attr": 17, 550 | "job_id": "352511791", 551 | "lan_task_id": "0", 552 | "layer_num": 1, 553 | "lights_report": [ 554 | { 555 | "mode": "on", 556 | "node": "chamber_light" 557 | }, 558 | { 559 | "mode": "flashing", 560 | "node": "work_light" 561 | }, 562 | { 563 | "mode": "on", 564 | "node": "chamber_light2" 565 | } 566 | ], 567 | "maintain": 3, 568 | "mapping": [ 569 | 65535, 570 | 65535, 571 | 65535, 572 | 65535, 573 | 65535, 574 | 65535, 575 | 65535, 576 | 257 577 | ], 578 | "mc_action": 0, 579 | "mc_err": 0, 580 | "mc_percent": 8, 581 | "mc_print_error_code": "0", 582 | "mc_print_stage": "2", 583 | "mc_print_sub_stage": 0, 584 | "mc_remaining_time": 313, 585 | "mc_stage": 2, 586 | "model_id": "US4932babe82ce32", 587 | "net": { 588 | "conf": 16, 589 | "info": [ 590 | { 591 | "ip": 2713495744, 592 | "mask": 16777215 593 | }, 594 | { 595 | "ip": 0, 596 | "mask": 0 597 | } 598 | ] 599 | }, 600 | "nozzle_diameter": "0.4", 601 | "nozzle_target_temper": 0, 602 | "nozzle_temper": 44, 603 | "nozzle_type": "stainless_steel", 604 | "online": { 605 | "ahb": true, 606 | "ext": true, 607 | "version": 8 608 | }, 609 | "percent": 8, 610 | "plate_cnt": 2, 611 | "plate_id": 1, 612 | "plate_idx": 1, 613 | "prepare_per": 100, 614 | "print_error": 0, 615 | "print_gcode_action": 0, 616 | "print_real_action": 0, 617 | "print_type": "cloud", 618 | "profile_id": "305233194", 619 | "project_id": "331855685", 620 | "queue": 0, 621 | "queue_est": 0, 622 | "queue_number": 0, 623 | "queue_sts": 0, 624 | "queue_total": 0, 625 | "remain_time": 313, 626 | "s_obj": [], 627 | "sdcard": true, 628 | "sequence_id": "2021", 629 | "spd_lvl": 2, 630 | "spd_mag": 100, 631 | "stat": "16382081F0", 632 | "state": 4, 633 | "stg": [ 634 | 29, 635 | 13, 636 | 4, 637 | 8, 638 | 14, 639 | 1, 640 | 3, 641 | 39 642 | ], 643 | "stg_cur": 0, 644 | "subtask_id": "675488755", 645 | "subtask_name": "QBRICK-Akkufach v3-SOLID-1", 646 | "task_id": "675488754", 647 | "total_layer_num": 435, 648 | "upgrade_state": { 649 | "ahb_new_version_number": "", 650 | "ams_new_version_number": "", 651 | "consistency_request": false, 652 | "dis_state": 0, 653 | "err_code": 0, 654 | "ext_new_version_number": "", 655 | "force_upgrade": false, 656 | "idx": 8, 657 | "idx2": 1579120524, 658 | "lower_limit": "00.00.00.00", 659 | "message": "", 660 | "module": "", 661 | "new_version_state": 1, 662 | "ota_new_version_number": "01.01.02.04", 663 | "progress": "0", 664 | "sequence_id": 0, 665 | "sn": "0948BB510200105", 666 | "status": "IDLE" 667 | }, 668 | "upload": { 669 | "file_size": 0, 670 | "finish_size": 0, 671 | "message": "Good", 672 | "oss_url": "", 673 | "progress": 0, 674 | "sequence_id": "0903", 675 | "speed": 0, 676 | "status": "idle", 677 | "task_id": "", 678 | "time_remaining": 0, 679 | "trouble_id": "" 680 | }, 681 | "ver": "20000", 682 | "vir_slot": [ 683 | { 684 | "bed_temp": "0", 685 | "bed_temp_type": "0", 686 | "cali_idx": -1, 687 | "cols": [ 688 | "FFFFFF00" 689 | ], 690 | "ctype": 0, 691 | "drying_temp": "0", 692 | "drying_time": "0", 693 | "id": "254", 694 | "nozzle_temp_max": "0", 695 | "nozzle_temp_min": "0", 696 | "remain": 0, 697 | "tag_uid": "0000000000000000", 698 | "total_len": 330000, 699 | "tray_color": "FFFFFF00", 700 | "tray_diameter": "1.75", 701 | "tray_id_name": "", 702 | "tray_info_idx": "", 703 | "tray_sub_brands": "", 704 | "tray_type": "", 705 | "tray_uuid": "00000000000000000000000000000000", 706 | "tray_weight": "0", 707 | "xcam_info": "000000000000000000000000" 708 | }, 709 | { 710 | "bed_temp": "0", 711 | "bed_temp_type": "0", 712 | "cali_idx": -1, 713 | "cols": [ 714 | "00000000" 715 | ], 716 | "ctype": 0, 717 | "drying_temp": "0", 718 | "drying_time": "0", 719 | "id": "255", 720 | "nozzle_temp_max": "0", 721 | "nozzle_temp_min": "0", 722 | "remain": 0, 723 | "tag_uid": "0000000000000000", 724 | "total_len": 330000, 725 | "tray_color": "00000000", 726 | "tray_diameter": "1.75", 727 | "tray_id_name": "", 728 | "tray_info_idx": "", 729 | "tray_sub_brands": "", 730 | "tray_type": "", 731 | "tray_uuid": "00000000000000000000000000000000", 732 | "tray_weight": "0", 733 | "xcam_info": "000000000000000000000000" 734 | } 735 | ], 736 | "vt_tray": { 737 | "bed_temp": "0", 738 | "bed_temp_type": "0", 739 | "cali_idx": -1, 740 | "cols": [ 741 | "00000000" 742 | ], 743 | "ctype": 0, 744 | "drying_temp": "0", 745 | "drying_time": "0", 746 | "id": "255", 747 | "nozzle_temp_max": "0", 748 | "nozzle_temp_min": "0", 749 | "remain": 0, 750 | "tag_uid": "0000000000000000", 751 | "total_len": 330000, 752 | "tray_color": "00000000", 753 | "tray_diameter": "1.75", 754 | "tray_id_name": "", 755 | "tray_info_idx": "", 756 | "tray_sub_brands": "", 757 | "tray_type": "", 758 | "tray_uuid": "00000000000000000000000000000000", 759 | "tray_weight": "0", 760 | "xcam_info": "000000000000000000000000" 761 | }, 762 | "wifi_signal": "-68dBm", 763 | "xcam": { 764 | "allow_skip_parts": false, 765 | "buildplate_marker_detector": true, 766 | "cfg": 224695, 767 | "first_layer_inspector": true, 768 | "halt_print_sensitivity": "medium", 769 | "print_halt": true, 770 | "printing_monitor": true, 771 | "spaghetti_detector": true 772 | }, 773 | "xcam_status": "0" 774 | } --------------------------------------------------------------------------------