├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── docs ├── logo │ └── textmode.txt └── screenshots │ ├── start_screen.png │ └── video_2023-03-24_10-19-02.gif ├── esp32.ld ├── include ├── README └── lv_conf.h ├── lib ├── README └── Shellminator_dev │ ├── CONTRIBUTING.md │ ├── Doxyfile │ ├── LICENSE │ ├── README.md │ ├── examples │ ├── Shellminator_Commander │ │ └── Shellminator_Commander.ino │ ├── Shellminator_ESP32_server_with_Commander │ │ └── Shellminator_ESP32_server_with_Commander.ino │ ├── Shellminator_ESP32_server_with_Commander_prompt │ │ └── Shellminator_ESP32_server_with_Commander_prompt.ino │ ├── Shellminator_PROGMEM_Logo │ │ └── Shellminator_PROGMEM_Logo.ino │ ├── Shellminator_execute │ │ └── Shellminator_execute.ino │ ├── Shellminator_key_override │ │ └── Shellminator_key_override.ino │ ├── Shellminator_multiple_ESP32_websocket │ │ └── Shellminator_multiple_ESP32_websocket.ino │ ├── Shellminator_simple │ │ └── Shellminator_simple.ino │ ├── Shellminator_simple_ESP32_server │ │ └── Shellminator_simple_ESP32_server.ino │ ├── Shellminator_simple_ESP32_websocket │ │ └── Shellminator_simple_ESP32_websocket.ino │ ├── Shellminator_simple_ESP8266_server │ │ └── Shellminator_simple_ESP8266_server.ino │ └── Shellminator_simple_ESP8266_websocket │ │ └── Shellminator_simple_ESP8266_websocket.ino │ ├── keywords.txt │ ├── library.properties │ ├── other │ └── xterm_based_webpage │ │ ├── LICENSE-xterm │ │ ├── LICENSE-xterm-web-links │ │ ├── close.svg │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo.svg │ │ ├── minifiedPage │ │ ├── index.html │ │ ├── index_data_url.html │ │ ├── xterm-addon-web-links.js │ │ ├── xterm.css │ │ └── xterm.js │ │ ├── ws_server.py │ │ ├── xterm-addon-web-links.js │ │ ├── xterm-addon-web-links.js.map │ │ ├── xterm.css │ │ ├── xterm.js │ │ └── xtermm.js.map │ └── src │ ├── Shellminator-Browser-Response.hpp │ ├── Shellminator-IO.cpp │ ├── Shellminator-IO.hpp │ ├── Shellminator-Settings.hpp │ ├── Shellminator.cpp │ ├── Shellminator.hpp │ └── external │ └── nayuki-qrcodegen │ ├── license.txt │ ├── qrcodegen.c │ └── qrcodegen.h ├── platformio.ini ├── src ├── apps │ ├── blum_app_home.cpp │ ├── blum_app_home.h │ ├── blum_app_settings.cpp │ ├── blum_app_settings.h │ ├── ftp │ │ ├── ftp_server.cpp │ │ └── ftp_server.hpp │ ├── launcher │ │ ├── app_template.cpp │ │ ├── apps.cpp │ │ ├── apps.hpp │ │ └── apps_test1.cpp │ ├── parser │ │ ├── parser.cpp │ │ ├── parser.hpp │ │ ├── wrench.cpp │ │ └── wrench.h │ └── terminal │ │ ├── command_parser.cpp │ │ ├── commands_disk.cpp │ │ ├── commands_disk.hpp │ │ ├── commands_internal.cpp │ │ ├── commands_internal.hpp │ │ ├── terminal.cpp │ │ ├── terminal.hpp │ │ └── text_editor.cpp ├── blum.cpp ├── core │ ├── blum_global.c │ ├── blum_global.h │ ├── blum_navigation.cpp │ ├── blum_navigation.h │ ├── blum_status_bar.cpp │ ├── blum_status_bar.h │ ├── blum_widgets.cpp │ ├── blum_widgets.h │ └── snippets.cpp ├── hardware │ ├── bluetooth │ │ ├── TerminalBluetooth.cpp │ │ └── bluetooth.cpp.ignore │ ├── boards │ │ ├── ESP2866_NodeMCU_V3.h │ │ ├── ESP32_2432S028R_BGR.h │ │ ├── ESP32_2432S028R_RGB.h │ │ ├── ESP32_3248S035C.h │ │ └── ESP32_3248S035R.h │ ├── choose.h │ ├── display │ │ ├── tft_ilI9341.cpp │ │ └── tft_st7796.cpp │ ├── hardware.cpp │ ├── hardware.h │ ├── touch │ │ ├── touch_gt911.cpp │ │ └── touch_xpt2046.cpp │ └── wifi │ │ ├── blum_app_wifi.cpp │ │ ├── blum_app_wifi.h │ │ ├── module_wifi.cpp │ │ └── module_wifi.hpp ├── main.cpp └── utils │ ├── HashMap.cpp │ ├── StringArray.cpp │ ├── StringArray.h │ └── utils.h └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "editor.tabSize": 4, 4 | "editor.detectIndentation": false, 5 | "editor.insertSpaces": true, 6 | "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0, AlignAfterOpenBracket: true, AlignConsecutiveAssignments: false, AlignConsecutiveDeclarations: false, AllowShortIfStatementsOnASingleLine: false, AllowShortLoopsOnASingleLine: false, BreakBeforeBinaryOperators: true, BraceWrapping: { AfterClass: true, AfterControlStatement: true, AfterEnum: true, AfterFunction: true, AfterNamespace: true, AfterStruct: true, AfterUnion: true, BeforeCatch: true, BeforeElse: true, IndentWidth: 4, SplitEmptyFunction: false, SplitEmptyRecord: false, SplitEmptyNamespace: false } }", 7 | "C_Cpp.clang_format_style": "file", 8 | "C_Cpp.clang_format_sortIncludes": false, 9 | 10 | "files.associations": { 11 | "*.c ": "cpp", 12 | "blum.c": "cpp", 13 | "blum_global.c": "cpp", 14 | "blum_status_bar.c": "cpp", 15 | "blumwifi.c": "cpp", 16 | "blum_wifi.c": "cpp", 17 | "blum_app_wifi.c": "cpp", 18 | "blum_app_home.c": "cpp", 19 | "blum_app_settings.c": "cpp", 20 | "blum_widgets.c": "cpp", 21 | "blum_navigation.c": "cpp", 22 | "hashmap.c": "cpp", 23 | "array": "cpp", 24 | "atomic": "cpp", 25 | "*.tcc": "cpp", 26 | "cctype": "cpp", 27 | "chrono": "cpp", 28 | "clocale": "cpp", 29 | "cmath": "cpp", 30 | "condition_variable": "cpp", 31 | "cstdarg": "cpp", 32 | "cstddef": "cpp", 33 | "cstdint": "cpp", 34 | "cstdio": "cpp", 35 | "cstdlib": "cpp", 36 | "cstring": "cpp", 37 | "ctime": "cpp", 38 | "cwchar": "cpp", 39 | "cwctype": "cpp", 40 | "deque": "cpp", 41 | "unordered_map": "cpp", 42 | "unordered_set": "cpp", 43 | "vector": "cpp", 44 | "exception": "cpp", 45 | "algorithm": "cpp", 46 | "functional": "cpp", 47 | "iterator": "cpp", 48 | "map": "cpp", 49 | "memory": "cpp", 50 | "memory_resource": "cpp", 51 | "numeric": "cpp", 52 | "optional": "cpp", 53 | "random": "cpp", 54 | "ratio": "cpp", 55 | "string": "cpp", 56 | "string_view": "cpp", 57 | "system_error": "cpp", 58 | "tuple": "cpp", 59 | "type_traits": "cpp", 60 | "utility": "cpp", 61 | "fstream": "cpp", 62 | "initializer_list": "cpp", 63 | "iomanip": "cpp", 64 | "iosfwd": "cpp", 65 | "istream": "cpp", 66 | "limits": "cpp", 67 | "mutex": "cpp", 68 | "new": "cpp", 69 | "ostream": "cpp", 70 | "sstream": "cpp", 71 | "stdexcept": "cpp", 72 | "streambuf": "cpp", 73 | "thread": "cpp", 74 | "cinttypes": "cpp", 75 | "typeinfo": "cpp", 76 | "list": "cpp" 77 | } 78 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *Attention:* 2 | This project is archived. 3 | As (better) option I would recommend you to use https://tactility.one/ 4 | 5 | 6 | 7 | ![B3OS](/docs/screenshots/start_screen.png) 8 | 9 | B3 Operating System 10 | 11 | A long awaited GUI and CLI operating system 12 | for ESP32 devices. 13 | 14 | 15 | This OS provides: 16 | 17 | + standard dialogs (e.g. select wifi network) 18 | + easy to use button/textarea, icons 19 | + keyboard on screen is automatically handled 20 | + linux-like cli available through WifI/telnet 21 | 22 | 23 | Creating apps should be easy and don't require 24 | compiling firmwares. In smartphones we just install 25 | apps and the same should be available for ESP32. 26 | 27 | 28 | ![Animated demonstration](/docs/screenshots/video_2023-03-24_10-19-02.gif) 29 | 30 | 31 | 32 | # Getting started 33 | 34 | This project is based on ESP32 boards. At the moment we include support for the cheap Sunton boards (15 euros in 2023) that you can purchase from this location: https://www.aliexpress.com/item/1005004502250619.html because they include everything needed (display, touch screen, WiFi and an ESP32 with enough capacity). 35 | 36 | If you get a Sunton ESP32 board, this project works straight away without changes. In case you need to add support for your device, look on this file to see if support is already added: https://github.com/radio3-network/B3OS/blob/main/src/hardware/choose.h and in case it is not listed, just open a ticket here so that we can help. 37 | 38 | Code is developed using PlatformIO under Linux. If you are running/compiling under Windows, please modify the path here with the full path so that it can work: https://github.com/radio3-network/B3OS/blob/main/platformio.ini#L19 39 | 40 | 41 | # Help needed 42 | 43 | Where help is needed: 44 | 45 | + Documentation: Add your experience getting started 46 | + Add support for your boards (LiLy, eInk, others). Look here https://github.com/radio3-network/B3OS/blob/main/src/hardware/choose.h 47 | + Add more CLI apps, look here: https://github.com/radio3-network/B3OS/tree/main/src/apps/terminal 48 | + Add a text editor for CLI: https://github.com/radio3-network/B3OS/blob/main/src/apps/terminal/text_editor.cpp 49 | + Add dynamic loading of binaries, look here: https://github.com/radio3-network/B3OS/blob/main/src/apps/launcher/apps.cpp 50 | + Bluetooth, connect keyboard or mouse. Look here: https://github.com/radio3-network/B3OS/blob/main/src/hardware/bluetooth/TerminalBluetooth.cpp 51 | 52 | Just write an issue about any topic you can work to implement and we'll talk about the questions, where you can find what is needed and how to start working on them. 53 | 54 | Other things you can help? 55 | 56 | Give ideas, write on the forum: https://github.com/orgs/radio3-network/discussions 57 | 58 | Everyone is welcome! Either writing, trying out or sharing the word already helps so much. 59 | 60 | Remember to star if you like this project. Extra points if you do a video review and let us know the link. :-) 61 | 62 | 63 | # CLI access 64 | 65 | To run linux-like command line you first need to configure the WiFi access. 66 | 67 | After you are connected, look on the serial console output to see the output with your IP address. 68 | 69 | From the command line type 'telnet' plus the IP address at port 23. 70 | 71 | An example would be 'telnet 192.168.178.110 23' 72 | 73 | When you are inside, type 'help' to see the available commands. 74 | 75 | At present the following functions are supported: 76 | + help (show available commands) 77 | + clear (clear the screen) 78 | + formatCard (format the SD/TF card) 79 | + echo (print a text line to console) 80 | + ls (list the files) 81 | + ll (list the files with details) 82 | + mkdir (make a directory) 83 | + cd (change to directory) 84 | + rm (remove file) 85 | + touch (create empty file) 86 | + reboot (reboot device) 87 | + wait (wait for n milliseconds) 88 | + whoami (prints current user) 89 | + version (prints current OS version) 90 | + logo (print OS logo) 91 | + beep (terminal beep) 92 | + download (download a file from internet) 93 | + print (show contents of a file) 94 | + sh (run a script with commands) 95 | + analogRead (read analog value from a PIN) 96 | + dateTime (returns NTP synchronized time) 97 | + digitalRead (reads the state of a pin) 98 | + digitalWrite (write the state of a pin) 99 | + ipconfig (show IP connection details) 100 | + random (returns a random number within a range) 101 | + uptime (how long the device is running) 102 | + wifiScan (show available wifi networks) 103 | + wifiStat (print wifi information) 104 | 105 | Upcoming commands: 106 | + ftpserver 107 | + run 108 | 109 | 110 | 111 | # Legal 112 | 113 | This project is fully open source and delivered under the Apache version 2.0 license. 114 | 115 | Licenses from third-party libraries and resources adopted by this project are applicable and compatible with the Apache 2.0 license terms and conditions. 116 | 117 | License: Apache-2.0 118 | Copyright (c) B3OS contributors 119 | 120 | 121 | # Thanks 122 | 123 | This project is only possible due to the work and effort of many other projects and people. 124 | On this section we try to list them. 125 | 126 | 127 | ## Espressif and Sunton 128 | For the incredibly powerful and affordable ESP32 boards 129 | + https://www.aliexpress.com/item/1005004502250619.html 130 | + https://www.espressif.com/ 131 | 132 | 133 | ## LVGL 134 | For making modern GUI windows available for Arduino 135 | + https://lvgl.io/ 136 | 137 | 138 | ## ESP32-SmartDisplay 139 | For making it possible and easy to write code 140 | for these Sunton boards. 141 | + https://github.com/rzeldent/esp32-smartdisplay 142 | 143 | 144 | ## Shellminator 145 | Terminal library for accessing the device from 146 | either the WiFi or a serial cable. 147 | + https://github.com/dani007200964/Shellminator 148 | 149 | 150 | ## Xila 151 | For the good conversations and perspective to serve as host for this project one day. 152 | + https://xila.dev/ 153 | -------------------------------------------------------------------------------- /docs/logo/textmode.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BBBBBBBBBBBBBBBBB 333333333333333 OOOOOOOOO SSSSSSSSSSSSSSS 5 | B::::::::::::::::B 3:::::::::::::::33 OO:::::::::OO SS:::::::::::::::S 6 | B::::::BBBBBB:::::B 3::::::33333::::::3 OO:::::::::::::OO S:::::SSSSSS::::::S 7 | BB:::::B B:::::B3333333 3:::::3O:::::::OOO:::::::OS:::::S SSSSSSS 8 | B::::B B:::::B 3:::::3O::::::O O::::::OS:::::S 9 | B::::B B:::::B 3:::::3O:::::O O:::::OS:::::S 10 | B::::BBBBBB:::::B 33333333:::::3 O:::::O O:::::O S::::SSSS 11 | B:::::::::::::BB 3:::::::::::3 O:::::O O:::::O SS::::::SSSSS 12 | B::::BBBBBB:::::B 33333333:::::3 O:::::O O:::::O SSS::::::::SS 13 | B::::B B:::::B 3:::::3O:::::O O:::::O SSSSSS::::S 14 | B::::B B:::::B 3:::::3O:::::O O:::::O S:::::S 15 | B::::B B:::::B 3:::::3O::::::O O::::::O S:::::S 16 | BB:::::BBBBBB::::::B3333333 3:::::3O:::::::OOO:::::::OSSSSSSS S:::::S 17 | B:::::::::::::::::B 3::::::33333::::::3 OO:::::::::::::OO S::::::SSSSSS:::::S 18 | B::::::::::::::::B 3:::::::::::::::33 OO:::::::::OO S:::::::::::::::SS 19 | BBBBBBBBBBBBBBBBB 333333333333333 OOOOOOOOO SSSSSSSSSSSSSSS 20 | 21 | 22 | 23 | 24 | ██████  ██████  ██████  ███████ 25 | ██   ██      ██ ██    ██ ██ 26 | ██████   █████  ██  ██ ███████ 27 | ██   ██      ██ ██  ██      ██ 28 | ██████  ██████   ██████  ███████ 29 | 30 | 31 | 32 | ██████╗ ██████╗ ██████╗ ███████╗ 33 | ██╔══██╗╚════██╗██╔═══██╗██╔════╝ 34 | ██████╔╝ █████╔╝██║ ██║███████╗ 35 | ██╔══██╗ ╚═══██╗██║ ██║╚════██║ 36 | ██████╔╝██████╔╝╚██████╔╝███████║ 37 | ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ 38 | 39 | 40 | 41 | 42 | `7MM"""Yp, .g8""8q. .M"""bgd 43 | MM Yb .dP' `YM. ,MI "Y 44 | MM dP pd""b. dM' `MM `MMb. 45 | MM"""bg.(O) `8b MM MM `YMMNq. 46 | MM `Y ,89 MM. ,MP . `MM 47 | MM ,9 ""Yb. `Mb. ,dP' Mb dM 48 | .JMMmmmd9 88 `"bmmd"' P"Ybmmd" 49 | (O) .M' 50 | bmmmd' 51 | 52 | 53 | 54 | ____ ____ ____ _____ 55 | | _ \___ \ / __ \ / ____| 56 | | |_) |__) | | | | (___ 57 | | _ <|__ <| | | |\___ \ 58 | | |_) |__) | |__| |____) | 59 | |____/____/ \____/|_____/ 60 | 61 | 62 | 63 | ᆖBᆖ3ᆖOᆖSᆖ 64 | 65 | ፧B፧3፧O፧S፧ 66 | 67 | ᆞBᆞ3ᆞOᆞSᆞ 68 | 69 | B⁣₃OS 70 | 71 | 🅱𝟑🅾🆂 72 | 73 | 𝑩𝟑𝑶𝑺 74 | 75 | 🄱3🄾🅂 76 | 77 | 78 | 79 | 80 | __/\\\\\\\\\\\\\_______/\\\\\\\\\\________/\\\\\__________/\\\\\\\\\\\___ 81 | _\/\\\/////////\\\___/\\\///////\\\_____/\\\///\\\______/\\\/////////\\\_ 82 | _\/\\\_______\/\\\__\///______/\\\____/\\\/__\///\\\___\//\\\______\///__ 83 | _\/\\\\\\\\\\\\\\__________/\\\//____/\\\______\//\\\___\////\\\_________ 84 | _\/\\\/////////\\\________\////\\\__\/\\\_______\/\\\______\////\\\______ 85 | _\/\\\_______\/\\\___________\//\\\_\//\\\______/\\\__________\////\\\___ 86 | _\/\\\_______\/\\\__/\\\______/\\\___\///\\\__/\\\_____/\\\______\//\\\__ 87 | _\/\\\\\\\\\\\\\/__\///\\\\\\\\\/______\///\\\\\/_____\///\\\\\\\\\\\/___ 88 | _\/////////////______\/////////__________\/////_________\///////////_____ 89 | 90 | 91 | 92 | 93 | ,-,---. ,,--. .---. 94 | '|___/ ,-. |`, | \___ 95 | ,| \ -< | | \ 96 | `-^---' `-' `---' `---' 97 | 98 | 99 | 100 | __ __ __ __ 101 | |__) _)/ \(_ 102 | |__) __)\__/__) 103 | 104 | 105 | 106 | ▀███▀▀▀██▄ ▄▄█▀▀██▄ ▄█▀▀▀█▄█ 107 | ██ ██ ▄██▀ ▀██▄██ ▀█ 108 | ██ ██ ██▀▀█▄ ██▀ ▀█████▄ 109 | ██▀▀▀█▄▄███ ▀████ ██ ▀█████▄ 110 | ██ ▀█ ▄████▄ ▄██ ▀██ 111 | ██ ▄█ ▀▀██▄▀██▄ ▄██▀█ ██ 112 | ▄████████ ██ ▀▀████▀▀ █▀█████▀ 113 | ███ ▄█▀ 114 | █████▀ 115 | 116 | 117 | ┏━━┓━┏━━━┓┏━━━┓┏━━━┓ 118 | ┃┏┓┃━┃┏━┓┃┃┏━┓┃┃┏━┓┃ 119 | ┃┗┛┗┓┗┛┏┛┃┃┃━┃┃┃┗━━┓ 120 | ┃┏━┓┃┏┓┗┓┃┃┃━┃┃┗━━┓┃ 121 | ┃┗━┛┃┃┗━┛┃┃┗━┛┃┃┗━┛┃ 122 | ┗━━━┛┗━━━┛┗━━━┛┗━━━┛ 123 | ━━━━━━━━━━━━━━━━━━━━ 124 | ━━━━━━━━━━━━━━━━━━━━ 125 | 126 | 127 | ░▒█▀▀▄░█▀▀█░▒█▀▀▀█░▒█▀▀▀█ 128 | ░▒█▀▀▄░░▒▀▄░▒█░░▒█░░▀▀▀▄▄ 129 | ░▒█▄▄█░█▄▄█░▒█▄▄▄█░▒█▄▄▄█ 130 | 131 | 132 | 133 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 134 | ░ ░░░░░░░░░░░░░░░░░░ ░░░░░░░░ ░░ 135 | ▒ ▒▒ ▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒ ▒▒▒ ▒▒▒▒ 136 | ▒ ▒▒▒ ▒▒▒▒▒ ▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ▒▒▒▒▒▒ 137 | ▓ ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓ 138 | ▓ ▓▓▓▓ ▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓ 139 | ▓ ▓▓▓▓▓ ▓▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓ ▓▓ ▓▓▓▓ 140 | █ █ ██ ███████ ████████ ██ 141 | █████████████████████████████████████████████ 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/screenshots/start_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radio3-network/B3OS/c7ba3083391eb68af87f0c8bd805043c102536cc/docs/screenshots/start_screen.png -------------------------------------------------------------------------------- /docs/screenshots/video_2023-03-24_10-19-02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radio3-network/B3OS/c7ba3083391eb68af87f0c8bd805043c102536cc/docs/screenshots/video_2023-03-24_10-19-02.gif -------------------------------------------------------------------------------- /esp32.ld: -------------------------------------------------------------------------------- 1 | /* The following lines define the memory regions and their sizes */ 2 | MEMORY 3 | { 4 | dram0_0_seg : org = 0x3F800000, len = 320K 5 | iram1_0_seg : org = 0x40100000, len = 96K 6 | irom0_0_seg : org = 0x40200000, len = 1M 7 | } 8 | 9 | /* The following lines define the sections of the program and the memory regions they belong to */ 10 | SECTIONS 11 | { 12 | .dram0.bss : 13 | { 14 | /* The following line specifies the memory region for this section */ 15 | *(.dram0.bss) 16 | /* The following line specifies that this section should be aligned to a 4-byte boundary */ 17 | . = ALIGN(4) 18 | } > dram0_0_seg 19 | } 20 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How Can I Contribute? 2 | 3 | ### Reporting Bugs 4 | 5 | Bugs are tracked as GitHub issues. If you find a problem, [create an issue](https://github.com/dani007200964/Shellminator/issues/new?assignees=&labels=bug&template=bug_report.md&title=) and provide the following information: 6 | * Use a clear and descriptive title for the issue to identify the problem. 7 | * Describe the exact steps which reproduce the problem in as many details as possible. 8 | * Provide specific examples to demonstrate the steps. 9 | * Provide a detailed description about the configuration which caused the problem. 10 | 11 | ### Suggesting Enhancements 12 | 13 | The best way to ask for help or propose a new idea is to [create a new issue](https://github.com/dani007200964/Shellminator/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=). 14 | Before creating enhancement suggestions, please check the enhancements related issues, as you might find out that you don't need to create one. 15 | 16 | * In the enhancement suggestion, please describe your idea as clear as possible. 17 | * Use a clear and descriptive title for the issue to identify the suggestion. 18 | * Provide a step-by-step description of the suggested enhancement in as many details as possible. 19 | * Describe the current behavior and explain which behavior you expected to see instead and why. 20 | 21 | ### Pull Requests 22 | 23 | 1. Check that your modifications works as expected. 24 | 2. If it's a new feature please provide an example with it. 25 | 3. Fill the template. 26 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Daniel Hajnal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/README.md: -------------------------------------------------------------------------------- 1 | # Shellminator ![Build Status](https://github.com/dani007200964/Shellminator/actions/workflows/compile-examples.yml/badge.svg?branch=main) 2 | 3 | Shellminator is a simple-to-use terminal interface library. You can use it with any __VT-100__ 4 | terminal emulator like [PuTTY](https://www.putty.org/), [Terraterm](https://ttssh2.osdn.jp/index.html.en) or [minicom](https://linux.die.net/man/1/minicom). 5 | With this library, you can create user-friendly command line interfaces for your embedded projects. 6 | Shellminator has command history support, that means you can browse your previous commands with the arrow keys on the keyboard. 7 | The library is Arduino compatible out of the box, but if you want to use it with other platforms, 8 | you can do that in Shellminator-IO source files. 9 | 10 | __Key changes in V1.2:__ 11 | * New shortcut keys. 12 | * Option to set the timeout of the internal client. 13 | * Advanced search functions( Ctrl-R, Page-Up, Page-Down ) 14 | * Banner text change option. 15 | * Path text change option. Also change it to blue color 16 | * Websocket channel 17 | * Terminal buzzer command, with mute option 18 | * QR-code generation 19 | * History command 20 | * Help command 21 | 22 | __Breaking changes in V1.2:__ 23 | * The response channel is now uses the Stream class. 24 | This way it is more flexible and you doesn't have to 25 | create a class for every peripheral. 26 | 27 | ## Video Tutorial( currently for the older version ) 28 | 29 | [![Tutorial VIdeo](https://img.youtube.com/vi/O2su8kXg1X8/0.jpg)](https://www.youtube.com/watch?v=O2su8kXg1X8) 30 | 31 | ## Documentation 32 | 33 | The full documentation can be found [here](https://dani007200964.github.io/Shellminator/html/index.html). 34 | 35 | ## Donation 36 | If this project help you reduce time to develop, you can give me a cup of coffee :coffee: :coffee: :coffee: 37 | 38 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?hosted_button_id=YFGZD78H6K2CS) 39 | 40 | ## Contributing 41 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 42 | 43 | Please make sure to update tests as appropriate. 44 | 45 | ## License & copyright 46 | © Daniel Hajnal 47 | 48 | :email: hajnal.daniel96@gmail.com 49 | 50 | Licensed under the [MIT License](LICENSE). 51 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_Commander/Shellminator_Commander.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example demonstrates how to attach a Commander-API based command 11 | * parser to a Shellminator object. 12 | * To use this example you need the Commander-API to be installed to your 13 | * Arduino IDE. 14 | */ 15 | 16 | #include "Shellminator.hpp" 17 | #include "Shellminator-IO.hpp" 18 | 19 | #include "Commander-API.hpp" 20 | #include "Commander-IO.hpp" 21 | 22 | // Create a Shellminator object, and initialize it to use Serial 23 | // If we use Commander, we don't need an execution function, 24 | // because Shellminator internally manages that. 25 | Shellminator shell( &Serial ); 26 | 27 | const char logo[] = 28 | 29 | " _____ __ ____ _ __ \r\n" 30 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 31 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 32 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 33 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 34 | "\r\n\033[0;37m" 35 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 36 | 37 | ; 38 | 39 | // We have to create an object from Commander class. 40 | Commander commander; 41 | 42 | // We have to create the prototype functions for our commands. 43 | // The arguments has to be the same for all command functions. 44 | void cat_func( char *args, Stream *response ); 45 | void dog_func( char *args, Stream *response ); 46 | void sum_func( char *args, Stream *response ); 47 | void led_func( char *args, Stream *response ); 48 | 49 | // Commander API-tree 50 | Commander::API_t API_tree[] = { 51 | apiElement( "cat", "Description for cat command.", cat_func ), 52 | apiElement( "dog", "Description for dog command.", dog_func ), 53 | apiElement( "led", "Toggle the buit-in LED.", led_func ), 54 | apiElement( "sum", "This function sums two number from the argument list.", sum_func ) 55 | }; 56 | 57 | void setup() { 58 | 59 | // initialize Serial with 115200 baudrate. 60 | Serial.begin( 115200 ); 61 | 62 | // If you using Atmega32U4, the code will wait, until 63 | // you open the serial port. 64 | while( !Serial ); 65 | 66 | // Clear the terminal 67 | shell.clear(); 68 | 69 | // Attach the logo. 70 | shell.attachLogo( logo ); 71 | 72 | // Print start message 73 | Serial.println( "Program begin..." ); 74 | 75 | // There is an option to attach a debug channel to Commander. 76 | // It can be handy to find any problems during the initialization 77 | // phase. In this example we will use Serial for this. 78 | commander.attachDebugChannel( &Serial ); 79 | 80 | // At start, Commander does not know anything about our commands. 81 | // We have to attach the API_tree array from the previous steps 82 | // to Commander to work properly. 83 | commander.attachTree( API_tree ); 84 | 85 | // Initialize Commander. 86 | commander.init(); 87 | 88 | shell.attachCommander( &commander ); 89 | 90 | // Initialize shell object. 91 | shell.begin( "arnold" ); 92 | 93 | } 94 | 95 | void loop() { 96 | 97 | shell.update(); 98 | 99 | } 100 | 101 | /// This is an example function for the cat command 102 | void cat_func(char *args, Stream *response ) 103 | { 104 | 105 | response -> print("Hello from cat function!\r\n"); 106 | 107 | } 108 | 109 | /// This is an example function for the dog command 110 | void dog_func(char *args, Stream *response ) 111 | { 112 | 113 | response -> print("Hello from dog function!\r\n"); 114 | 115 | } 116 | 117 | /// This is an example function for the led command 118 | void led_func(char *args, Stream *response ) 119 | { 120 | 121 | // Toggle your LED pin here, if you have on your board 122 | // digitalWrite( LED_PIN, !digitalRead( LED_PIN ) ); 123 | response -> print("LED toggle!\r\n"); 124 | 125 | } 126 | 127 | /// This is an example function for the sum command 128 | void sum_func(char *args, Stream *response ) 129 | { 130 | 131 | // These variables will hold the value of the 132 | // two numbers, that has to be summed. 133 | int a = 0; 134 | int b = 0; 135 | 136 | // This variable will hold the result of the 137 | // argument parser. 138 | int argResult; 139 | 140 | // This variable will hold the sum result. 141 | int sum = 0; 142 | 143 | argResult = sscanf( args, "%d %d", &a, &b ); 144 | 145 | // We have to check that we parsed successfully the two 146 | // numbers from the argument string. 147 | if( argResult != 2 ){ 148 | 149 | // If we could not parse two numbers, we have an argument problem. 150 | // We print out the problem to the response channel. 151 | response -> print( "Argument error! Two numbers required, separated with a blank space.\r\n" ); 152 | 153 | // Sadly we have to stop the command execution and return. 154 | return; 155 | 156 | } 157 | 158 | // Calculate the sum. 159 | sum = a + b; 160 | 161 | // Print out the result. 162 | response -> print( a ); 163 | response -> print( " + " ); 164 | response -> print( b ); 165 | response -> print( " = " ); 166 | response -> println( sum ); 167 | 168 | } 169 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_ESP32_server_with_Commander/Shellminator_ESP32_server_with_Commander.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Oct. 2 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.10.02 8 | * 9 | */ 10 | 11 | #include 12 | #include "esp_wifi.h" 13 | 14 | #include "Shellminator.hpp" 15 | #include "Shellminator-IO.hpp" 16 | 17 | // Necessary includes 18 | #include "Commander-API.hpp" 19 | #include "Commander-IO.hpp" 20 | 21 | #define SERVER_PORT 23 22 | 23 | // WiFi credentials. 24 | const char* ssid = "your-wifi-ssid"; 25 | const char* password = "your-wifi-pass"; 26 | 27 | // Create an instance of the server. 28 | // It will be available on port 23. 29 | WiFiServer server( SERVER_PORT ); 30 | 31 | // Create a Shellminator object, and initialize it to use WiFiServer 32 | Shellminator shell( &server ); 33 | 34 | const char logo[] = 35 | 36 | " _____ __ ____ _ __ \r\n" 37 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 38 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 39 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 40 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 41 | " \r\n" 42 | ; 43 | 44 | // We have to create an object from Commander class. 45 | Commander commander; 46 | 47 | // We have to create the prototype functions for our commands. 48 | // The arguments has to be the same for all command functions. 49 | void cat_func( char *args, Stream *response ); 50 | void dog_func( char *args, Stream *response ); 51 | void sum_func( char *args, Stream *response ); 52 | void led_func( char *args, Stream *response ); 53 | 54 | // Commander API-tree 55 | Commander::API_t API_tree[] = { 56 | apiElement( "cat", "Description for cat command.", cat_func ), 57 | apiElement( "dog", "Description for dog command.", dog_func ), 58 | apiElement( "led", "Toggle the buit-in LED.", led_func ), 59 | apiElement( "sum", "This function sums two number from the argument list.", sum_func ) 60 | }; 61 | 62 | void setup() { 63 | 64 | // Initialize Serial with 115200 baudrate. 65 | Serial.begin( 115200 ); 66 | 67 | // If you using Atmega32U4, the code will wait, until 68 | // you open the serial port. 69 | while( !Serial ); 70 | 71 | // Clear the terminal 72 | shell.clear(); 73 | 74 | // Attach the logo. 75 | shell.attachLogo( logo ); 76 | 77 | // Print start message 78 | Serial.println( "Program begin..." ); 79 | 80 | // WiFi configuration section 81 | Serial.print( "Connect to WiFi: " ); 82 | Serial.print( ssid ); 83 | 84 | WiFi.mode( WIFI_STA ); 85 | WiFi.setSleep(WIFI_PS_NONE); 86 | WiFi.begin( ssid, password ); 87 | 88 | while( WiFi.status() != WL_CONNECTED ){ 89 | 90 | delay( 1000 ); 91 | Serial.print( "." ); 92 | 93 | } 94 | 95 | shell.beginServer(); 96 | 97 | Serial.println( " [ OK ]" ); 98 | 99 | Serial.println( "Connected!" ); 100 | Serial.print( "Device IP: " ); 101 | Serial.print( WiFi.localIP() ); 102 | Serial.print( " at port: " ); 103 | Serial.println( SERVER_PORT ); 104 | 105 | 106 | // There is an option to attach a debug channel to Commander. 107 | // It can be handy to find any problems during the initialization 108 | // phase. In this example we will use Serial for this. 109 | commander.attachDebugChannel( &Serial ); 110 | 111 | // At start, Commander does not know anything about our commands. 112 | // We have to attach the API_tree array from the previous steps 113 | // to Commander to work properly. 114 | commander.attachTree( API_tree ); 115 | 116 | // Initialize Commander. 117 | commander.init(); 118 | 119 | shell.attachCommander( &commander ); 120 | 121 | // initialize shell object. 122 | shell.begin( "arnold" ); 123 | 124 | } 125 | 126 | void loop() { 127 | 128 | shell.update(); 129 | 130 | } 131 | 132 | /// This is an example function for the cat command 133 | void cat_func(char *args, Stream *response ) 134 | { 135 | 136 | response -> print("Hello from cat function!\r\n"); 137 | 138 | } 139 | 140 | /// This is an example function for the dog command 141 | void dog_func(char *args, Stream *response ) 142 | { 143 | 144 | response -> print("Hello from dog function!\r\n"); 145 | 146 | } 147 | 148 | /// This is an example function for the led command 149 | void led_func(char *args, Stream *response ) 150 | { 151 | 152 | // Toggle your LED pin here, if you have on your board 153 | // digitalWrite( LED_PIN, !digitalRead( LED_PIN ) ); 154 | response -> print("LED toggle!\r\n"); 155 | 156 | } 157 | 158 | /// This is an example function for the sum command 159 | void sum_func(char *args, Stream *response ) 160 | { 161 | 162 | // These variables will hold the value of the 163 | // two numbers, that has to be summed. 164 | int a = 0; 165 | int b = 0; 166 | 167 | // This variable will hold the result of the 168 | // argument parser. 169 | int argResult; 170 | 171 | // This variable will hold the sum result. 172 | int sum = 0; 173 | 174 | argResult = sscanf( args, "%d %d", &a, &b ); 175 | 176 | // We have to check that we parsed successfully the two 177 | // numbers from the argument string. 178 | if( argResult != 2 ){ 179 | 180 | // If we could not parse two numbers, we have an argument problem. 181 | // We print out the problem to the response channel. 182 | response -> print( "Argument error! Two numbers required, separated with a blank space.\r\n" ); 183 | 184 | // Sadly we have to stop the command execution and return. 185 | return; 186 | 187 | } 188 | 189 | // Calculate the sum. 190 | sum = a + b; 191 | 192 | // Print out the result. 193 | response -> print( a ); 194 | response -> print( " + " ); 195 | response -> print( b ); 196 | response -> print( " = " ); 197 | response -> println( sum ); 198 | 199 | } 200 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_ESP32_server_with_Commander_prompt/Shellminator_ESP32_server_with_Commander_prompt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Oct. 2 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2023.03.29 8 | * 9 | */ 10 | 11 | /* 12 | * This eample shows, how to use the prompts. 13 | * It is designed to work with ESP32. 14 | */ 15 | 16 | #include 17 | #include "esp_wifi.h" 18 | 19 | #include "Shellminator.hpp" 20 | #include "Shellminator-IO.hpp" 21 | 22 | // Necessary includes 23 | #include "Commander-API.hpp" 24 | #include "Commander-IO.hpp" 25 | 26 | #define SERVER_PORT 23 27 | 28 | // WiFi credentials. 29 | const char* ssid = "your-wifi-ssid"; 30 | const char* password = "your-wifi-pass"; 31 | 32 | // Create an instance of the server. 33 | // It will be available on port 23. 34 | WiFiServer server( SERVER_PORT ); 35 | 36 | // Create a Shellminator object, and initialize it to use WiFiServer 37 | Shellminator shell( &server ); 38 | 39 | const char logo[] = 40 | 41 | " _____ __ ____ _ __ \r\n" 42 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 43 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 44 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 45 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 46 | " \r\n" 47 | ; 48 | 49 | // We have to create an object from Commander class. 50 | Commander commander; 51 | 52 | // We have to create the prototype functions for our commands. 53 | // The arguments has to be the same for all command functions. 54 | void name_func( char *args, Stream *response ); 55 | void reboot_func( char *args, Stream *response ); 56 | void sudo_func( char *args, Stream *response ); 57 | void sum_func( char *args, Stream *response ); 58 | 59 | // Commander API-tree 60 | Commander::API_t API_tree[] = { 61 | apiElement( "name", "Enter a name and echo it back.", name_func ), 62 | apiElement( "reboot", "Reboot command with prompt. To disable prompt use like this: reboot now", reboot_func ), 63 | apiElement( "sudo", "The worst sudo ever. It demonstrates how to hide typed characters.", sudo_func ), 64 | apiElement( "sum", "This function sums two number from a prompt.", sum_func ) 65 | }; 66 | 67 | void setup() { 68 | 69 | // Initialize Serial with 115200 baudrate. 70 | Serial.begin( 115200 ); 71 | 72 | // If you using Atmega32U4, the code will wait, until 73 | // you open the serial port. 74 | while( !Serial ); 75 | 76 | // Clear the terminal 77 | shell.clear(); 78 | 79 | // Attach the logo. 80 | shell.attachLogo( logo ); 81 | 82 | // Print start message 83 | Serial.println( "Program begin..." ); 84 | 85 | // WiFi configuration section 86 | Serial.print( "Connect to WiFi: " ); 87 | Serial.print( ssid ); 88 | 89 | WiFi.mode( WIFI_STA ); 90 | WiFi.setSleep(WIFI_PS_NONE); 91 | WiFi.begin( ssid, password ); 92 | 93 | while( WiFi.status() != WL_CONNECTED ){ 94 | 95 | delay( 1000 ); 96 | Serial.print( "." ); 97 | 98 | } 99 | 100 | shell.beginServer(); 101 | 102 | Serial.println( " [ OK ]" ); 103 | 104 | Serial.println( "Connected!" ); 105 | Serial.print( "Device IP: " ); 106 | Serial.print( WiFi.localIP() ); 107 | Serial.print( " at port: " ); 108 | Serial.println( SERVER_PORT ); 109 | 110 | 111 | // There is an option to attach a debug channel to Commander. 112 | // It can be handy to find any problems during the initialization 113 | // phase. In this example we will use Serial for this. 114 | commander.attachDebugChannel( &Serial ); 115 | 116 | // At start, Commander does not know anything about our commands. 117 | // We have to attach the API_tree array from the previous steps 118 | // to Commander to work properly. 119 | commander.attachTree( API_tree ); 120 | 121 | // Initialize Commander. 122 | commander.init(); 123 | 124 | shell.attachCommander( &commander ); 125 | 126 | // initialize shell object. 127 | shell.begin( "arnold" ); 128 | 129 | } 130 | 131 | void loop() { 132 | 133 | shell.update(); 134 | 135 | } 136 | 137 | /// This is an example function for the name command 138 | void name_func(char *args, Stream *response ) 139 | { 140 | 141 | char nameBuff[ 30 ]; 142 | int inputResult; 143 | 144 | inputResult = Shellminator::input( response, sizeof( nameBuff ), nameBuff, "Please enter your name: ", 0 ); 145 | 146 | if( inputResult ){ 147 | 148 | response -> print("Your name is: "); 149 | response -> println( nameBuff ); 150 | 151 | } 152 | 153 | else{ 154 | response -> println( "Prompt error!" ); 155 | } 156 | 157 | } 158 | 159 | /// This is an example function for the reboot command 160 | void reboot_func(char *args, Stream *response ) 161 | { 162 | 163 | if( strcmp( args, "now" ) == 0 ){ 164 | response -> println( "Please wait, the system will reboot..." ); 165 | return; 166 | } 167 | 168 | response -> print("Reboot the system now?\r\n[ y / n ]: "); 169 | 170 | if( Shellminator::waitForKey( response, "yY", 30000 ) ){ 171 | response -> println( "Please wait, the system will reboot..." ); 172 | } 173 | 174 | else{ 175 | response -> println( "Reboot aborted!" ); 176 | } 177 | 178 | } 179 | 180 | /// This is an example function for the sudo command 181 | void sudo_func(char *args, Stream *response ) 182 | { 183 | 184 | char pwBuff[ 30 ]; 185 | int inputResult; 186 | 187 | inputResult = Shellminator::input( response, sizeof( pwBuff ), pwBuff, "Please enter your password: ", 60000, true ); 188 | 189 | if( inputResult > 0 ){ 190 | 191 | response -> print("Your password is: "); 192 | response -> println( pwBuff ); 193 | 194 | } 195 | 196 | else{ 197 | response -> println( "Password error!" ); 198 | } 199 | 200 | } 201 | 202 | /// This is an example function for the sum command 203 | void sum_func(char *args, Stream *response ) 204 | { 205 | 206 | // These variables will hold the value of the 207 | // two numbers, that has to be summed. 208 | int a = 0; 209 | int b = 0; 210 | 211 | // This variable will hold the result of the 212 | // argument parser. 213 | int argResult; 214 | 215 | // This variable will hold the sum result. 216 | int sum = 0; 217 | 218 | // Buffer for number readings. 219 | char aBuff[ 6 ]; 220 | char bBuff[ 6 ]; 221 | 222 | // Try to read the first number. 223 | argResult = Shellminator::input( response, sizeof( aBuff ), aBuff, "First number: ", 0 ); 224 | 225 | // Check if the prompt was filled with something. 226 | if( argResult < 1 ){ 227 | 228 | response -> println( "No input!" ); 229 | return; 230 | } 231 | 232 | // Parse it as a number. 233 | argResult = sscanf( aBuff, "%d", &a ); 234 | 235 | // Check if it is a number. 236 | if( argResult != 1 ){ 237 | 238 | response -> print( "Argument error! It has to be a number!\r\n" ); 239 | 240 | // Sadly we have to stop the command execution and return. 241 | return; 242 | 243 | } 244 | 245 | // Try to read the first number. 246 | argResult = Shellminator::input( response, sizeof( bBuff ), bBuff, "Second number: ", 0 ); 247 | 248 | // Check if the prompt was filled with something. 249 | if( argResult < 1 ){ 250 | 251 | response -> println( "No input!" ); 252 | return; 253 | } 254 | 255 | // Parse it as a number. 256 | argResult = sscanf( bBuff, "%d", &b ); 257 | 258 | // Check if it is a number. 259 | if( argResult != 1 ){ 260 | 261 | response -> print( "Argument error! It has to be a number!\r\n" ); 262 | 263 | // Sadly we have to stop the command execution and return. 264 | return; 265 | 266 | } 267 | 268 | // Calculate the sum. 269 | sum = a + b; 270 | 271 | // Print out the result. 272 | response -> print( a ); 273 | response -> print( " + " ); 274 | response -> print( b ); 275 | response -> print( " = " ); 276 | response -> println( sum ); 277 | 278 | } 279 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_PROGMEM_Logo/Shellminator_PROGMEM_Logo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example shows a simple setup forShellminator. It will create an 11 | * interactive interface, but it does not execute any command. 12 | * See Shellminator_execute example for further information. 13 | * 14 | * Also it shows how to store the logo in the FLASH memory to save some RAM. 15 | */ 16 | 17 | #include "Shellminator.hpp" 18 | #include "Shellminator-IO.hpp" 19 | 20 | // Create a Shellminator object, and initialize it to use Serial 21 | Shellminator shell( &Serial ); 22 | 23 | 24 | 25 | void setup() { 26 | 27 | // Initialize Serial with 115200 baudrate. 28 | Serial.begin( 115200 ); 29 | 30 | // If you using Atmega32U4, the code will wait, until 31 | // you open the serial port. 32 | while( !Serial ); 33 | 34 | // Clear the terminal 35 | shell.clear(); 36 | 37 | __FlashStringHelper * logo = F( 38 | 39 | " _____ __ ____ _ __ \r\n" 40 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 41 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 42 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 43 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 44 | "\r\n\033[0;37m" 45 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 46 | 47 | ); 48 | 49 | // Attach the logo. 50 | shell.attachLogo( logo ); 51 | 52 | // Print start message 53 | Serial.println( "Program begin..." ); 54 | 55 | // initialize shell object. 56 | shell.begin( "arnold" ); 57 | 58 | } 59 | 60 | void loop() { 61 | 62 | shell.update(); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_execute/Shellminator_execute.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example demonstrates how to create and use an execution function. 11 | * The executin function just prints out the command, but not execute it. 12 | * To execute the commands, you need a command parser. You can create your 13 | * own or use Commander as shown in Shellminator_Commander example. 14 | */ 15 | 16 | #include "Shellminator-IO.hpp" 17 | #include "Shellminator.hpp" 18 | 19 | // Prototype to a function that will be called every time 20 | // when you press the enter key. 21 | void executionFunction( char* command ); 22 | 23 | // Create a Shellminator object, and initialize it to use Serial 24 | Shellminator shell( &Serial, executionFunction ); 25 | 26 | const char logo[] = 27 | 28 | " _____ __ ____ _ __ \r\n" 29 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 30 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 31 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 32 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 33 | "\r\n\033[0;37m" 34 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 35 | 36 | ; 37 | 38 | void setup() { 39 | 40 | // Initialize Serial with 115200 baudrate. 41 | Serial.begin( 115200 ); 42 | 43 | // If you using Atmega32U4, the code will wait, until 44 | // you open the serial port. 45 | while( !Serial ); 46 | 47 | // Clear the terminal 48 | shell.clear(); 49 | 50 | // Attach the logo. 51 | shell.attachLogo( logo ); 52 | 53 | // Print start message 54 | Serial.println( "Program begin..." ); 55 | 56 | // initialize shell object. 57 | shell.begin( "arnold" ); 58 | 59 | } 60 | 61 | void loop() { 62 | 63 | shell.update(); 64 | 65 | } 66 | 67 | void executionFunction( char* command ){ 68 | 69 | Serial.println( "Execution function called!" ); 70 | Serial.print( "Received command: " ); 71 | Serial.println( command ); 72 | 73 | } 74 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_key_override/Shellminator_key_override.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example demonstrates how to override key behaviour. It can be usefull 11 | * when you build a robot for example, because you can controll it with arrow 12 | * keys. Also you can attach an abort handler to stop that robot. 13 | */ 14 | 15 | #include "Shellminator.hpp" 16 | #include "Shellminator-IO.hpp" 17 | 18 | // Prototype to a function that will be called every time 19 | // when you press the enter key. 20 | void executionFunction( char* command ); 21 | 22 | // Prototype to a function that will be called every time 23 | // when you press the up arrow key. 24 | void upArrowOverride(); 25 | 26 | // Prototype to a function that will be called every time 27 | // when you press the down arrow key. 28 | void downArrowOverride(); 29 | 30 | // Prototype to a function that will be called every time 31 | // when you press the left arrow key. 32 | void leftArrowOverride(); 33 | 34 | // Prototype to a function that will be called every time 35 | // when you press the right arrow key. 36 | void rightArrowOverride(); 37 | 38 | // Prototype to a function that will be called every time 39 | // when you press the abort key( ctrl+c ). 40 | void abortHandler(); 41 | 42 | // Create a Shellminator object, and initialise it to use Serial 43 | Shellminator shell( &Serial, executionFunction ); 44 | 45 | const char logo[] = 46 | 47 | " _____ __ ____ _ __ \r\n" 48 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 49 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 50 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 51 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 52 | " \r\n" 53 | 54 | ; 55 | 56 | void setup() { 57 | 58 | // Initialise Serial with 115200 baudrate. 59 | Serial.begin( 115200 ); 60 | 61 | // Wait for connection. 62 | while( !Serial ); 63 | 64 | // Clear the terminal 65 | shell.clear(); 66 | 67 | // Attach the logo. 68 | shell.attachLogo( logo ); 69 | 70 | // Override key functions 71 | shell.overrideUpArrow( upArrowOverride ); 72 | shell.overrideDownArrow( downArrowOverride ); 73 | shell.overrideLeftArrow( leftArrowOverride ); 74 | shell.overrideRightArrow( rightArrowOverride ); 75 | 76 | shell.overrideAbortKey( abortHandler ); 77 | 78 | // Print start message 79 | Serial.println( "Program begin..." ); 80 | 81 | // Initialise shell object. 82 | shell.begin( "arnold" ); 83 | 84 | } 85 | 86 | void loop() { 87 | 88 | shell.update(); 89 | 90 | } 91 | 92 | void executionFunction( char* command ){ 93 | 94 | Serial.println( "Execution function called!" ); 95 | Serial.print( "Received command: " ); 96 | Serial.println( command ); 97 | 98 | } 99 | 100 | void upArrowOverride(){ 101 | 102 | Serial.println(); 103 | Serial.println( "Up arrow function!" ); 104 | 105 | } 106 | 107 | void downArrowOverride(){ 108 | 109 | Serial.println(); 110 | Serial.println( "Down arrow function!" ); 111 | 112 | } 113 | 114 | void leftArrowOverride(){ 115 | 116 | Serial.println(); 117 | Serial.println( "Left arrow function!" ); 118 | 119 | } 120 | 121 | void rightArrowOverride(){ 122 | 123 | Serial.println(); 124 | Serial.println( "Right arrow function!" ); 125 | 126 | } 127 | 128 | void abortHandler(){ 129 | 130 | Serial.println(); 131 | Serial.println( "Emergency stop!" ); 132 | Serial.println( "Reset key functions to normal!" ); 133 | 134 | shell.freeUpArrow(); 135 | shell.freeDownArrow(); 136 | shell.freeLeftArrow(); 137 | shell.freeRightArrow(); 138 | 139 | } 140 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_multiple_ESP32_websocket/Shellminator_multiple_ESP32_websocket.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sept 10 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.09.30 8 | * 9 | * This example shows how to use the websocket implemenation of Shellminator 10 | * with multiple clients. 11 | */ 12 | 13 | #include // <- https://github.com/Links2004/arduinoWebSockets 14 | #include 15 | #include 16 | #include 17 | #include "esp_wifi.h" 18 | 19 | #include "Shellminator.hpp" 20 | #include "Shellminator-Browser-Response.hpp" // <- It contains the webpage data 21 | 22 | // Websocket port for Shellminator. 23 | #define WEBSOCKET_PORT 81 24 | 25 | // Webserver port for webpage and contents. 26 | #define WEBSERVER_PORT 80 27 | 28 | // WiFi credentials. 29 | const char* ssid = "your-wifi-ssid"; 30 | const char* password = "your-wifi-pass"; 31 | 32 | // Create websocket object. 33 | WebSocketsServer webSocket = WebSocketsServer( WEBSOCKET_PORT ); 34 | 35 | // Create webserver object. 36 | WebServer server( WEBSERVER_PORT ); 37 | 38 | // Create an array of Shellminator objects, and initialize them to 39 | // use WebSocketsServer. Each have a different ID starting from 0. 40 | Shellminator shell[ 3 ] = { 41 | Shellminator( &webSocket, 0 ), 42 | Shellminator( &webSocket, 1 ), 43 | Shellminator( &webSocket, 2 ) 44 | }; 45 | 46 | // Shellminator logo. 47 | const char logo[] = 48 | 49 | " _____ __ ____ _ __ \r\n" 50 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 51 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 52 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 53 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 54 | "\r\n\033[0;37m" 55 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 56 | 57 | ; 58 | 59 | // This function generates a response for the index page. 60 | void handleIndex(){ 61 | server.send_P(200, "text/html", shellminator_html_response, shellminator_html_response_len ); 62 | } 63 | 64 | // This function generates a response for /xterm.js 65 | void handleXtermJs(){ 66 | server.send_P(200, "application/javascript", shellminator_xterm_js_response, shellminator_xterm_js_response_len ); 67 | } 68 | 69 | // This function generates a response for /xterm.css 70 | void handleXtermCss(){ 71 | server.send_P(200, "text/css", shellminator_xterm_css_response, shellminator_xterm_css_response_len ); 72 | } 73 | 74 | // This function generates a response for xterm-addon-web-links.js 75 | void handleXtermAddonWebLinks(){ 76 | server.send_P(200, "application/javascript", shellminator_xterm_addon_web_links_js_response, shellminator_xterm_addon_web_links_js_response_len ); 77 | } 78 | 79 | // This function generates a response for everything else. 80 | void handleNotFound() { 81 | String message = "File Not Found\n\n"; 82 | message += "URI: "; 83 | message += server.uri(); 84 | message += "\nMethod: "; 85 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 86 | message += "\nArguments: "; 87 | message += server.args(); 88 | message += "\n"; 89 | for (uint8_t i = 0; i < server.args(); i++) { 90 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 91 | } 92 | server.send(404, "text/plain", message); 93 | } 94 | 95 | // This function will be called on websocket event. 96 | void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { 97 | 98 | // This implementation works with multiple clients. You have to check if you 99 | // are in the available range of clients. 100 | if( num > 2 ){ 101 | 102 | // If we ran out of available clients, send 103 | // the problem to the client, than close it. 104 | webSocket.sendTXT( num, "\r\nNo more connections allowed!\r\n" ); 105 | webSocket.disconnect( num ); 106 | return; 107 | 108 | } 109 | 110 | // Depending on the event, we have to decide the next action. 111 | switch( type ){ 112 | 113 | // The client disconnected. 114 | case WStype_DISCONNECTED: 115 | 116 | // Currently, just print out the event. 117 | Serial.printf("[%u] Disconnected!\n", num); 118 | break; 119 | 120 | // New connection. 121 | case WStype_CONNECTED: 122 | 123 | // Clear the screen and print the logo with the banner. 124 | shell[ num ].clear(); 125 | shell[ num ].drawLogo(); 126 | shell[ num ].printBanner(); 127 | break; 128 | 129 | // Text data incoming 130 | case WStype_TEXT: 131 | 132 | // Print to serial, just for debug. 133 | Serial.printf("[%u] get Text: %s\n", num, payload); 134 | 135 | // In the Shellminator-IO files, the websocket channel 136 | // is implemented as a circular buffer. The incoming data 137 | // from the clients has to be pushed to this circular buffer 138 | // in the websocket event. 139 | shell.webSocketPush( payload, length ); 140 | break; 141 | 142 | } 143 | 144 | } 145 | 146 | void setup() { 147 | 148 | // Initialize Serial with 115200 baudrate. 149 | Serial.begin( 115200 ); 150 | 151 | // If you using Atmega32U4, the code will wait, until 152 | // you open the serial port. 153 | while( !Serial ); 154 | 155 | // Attach the logo. 156 | shell[ 0 ].attachLogo( logo ); 157 | shell[ 1 ].attachLogo( logo ); 158 | shell[ 2 ].attachLogo( logo ); 159 | 160 | // Print start message 161 | Serial.println( "Program begin..." ); 162 | 163 | // WiFi configuration section 164 | Serial.print( "Connect to WiFi: " ); 165 | Serial.print( ssid ); 166 | 167 | WiFi.mode( WIFI_STA ); 168 | WiFi.setSleep(WIFI_PS_NONE); 169 | WiFi.begin( ssid, password ); 170 | 171 | while( WiFi.status() != WL_CONNECTED ){ 172 | 173 | delay( 1000 ); 174 | Serial.print( "." ); 175 | 176 | } 177 | 178 | Serial.println( " [ OK ]" ); 179 | 180 | Serial.println( "Connected!" ); 181 | Serial.print( "Device IP: " ); 182 | Serial.println( WiFi.localIP() ); 183 | 184 | // Give each shell object an identifier. 185 | // It is only for visual purpose. 186 | shell[ 0 ].setBannerPathText( "ws-0 $" ); 187 | shell[ 1 ].setBannerPathText( "ws-1 $" ); 188 | shell[ 2 ].setBannerPathText( "ws-2 $" ); 189 | 190 | // initialize shell objects. 191 | shell[0].begin( "arnold" ); 192 | shell[1].begin( "arnold" ); 193 | shell[2].begin( "arnold" ); 194 | 195 | // Attach page handlers. 196 | server.on("/", handleIndex); 197 | server.on("/xterm.js", handleXtermJs); 198 | server.on("/xterm.css", handleXtermCss); 199 | server.on("/xterm-addon-web-links.js", handleXtermAddonWebLinks); 200 | server.onNotFound(handleNotFound); 201 | 202 | // Start webserver. 203 | server.begin(); 204 | 205 | // Start websocket server. 206 | webSocket.begin(); 207 | 208 | // Attach the webSocketEvent function to the websocket server. 209 | webSocket.onEvent(webSocketEvent); 210 | 211 | } 212 | 213 | void loop() { 214 | 215 | // Process everything. 216 | shell[ 0 ].update(); 217 | shell[ 1 ].update(); 218 | shell[ 2 ].update(); 219 | server.handleClient(); 220 | webSocket.loop(); 221 | 222 | // Give some time to the other tasks. 223 | delay( 2 ); 224 | 225 | } 226 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_simple/Shellminator_simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example shows a simple setup forShellminator. It will create an 11 | * interactive interface, but it does not execute any command. 12 | * See Shellminator_execute example for further information. 13 | */ 14 | 15 | #include "Shellminator.hpp" 16 | #include "Shellminator-IO.hpp" 17 | 18 | // Create a Shellminator object, and initialize it to use Serial 19 | Shellminator shell( &Serial ); 20 | 21 | 22 | const char logo[] = 23 | 24 | " _____ __ ____ _ __ \r\n" 25 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 26 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 27 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 28 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 29 | "\r\n\033[0;37m" 30 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 31 | 32 | ; 33 | 34 | 35 | void setup() { 36 | 37 | // Initialize Serial with 115200 baudrate. 38 | Serial.begin( 115200 ); 39 | 40 | // If you using Atmega32U4, the code will wait, until 41 | // you open the serial port. 42 | while( !Serial ); 43 | 44 | // Clear the terminal 45 | shell.clear(); 46 | 47 | // Attach the logo. 48 | shell.attachLogo( logo ); 49 | 50 | // Print start message 51 | Serial.println( "Program begin..." ); 52 | 53 | // initialize shell object. 54 | shell.begin( "arnold" ); 55 | 56 | } 57 | 58 | void loop() { 59 | 60 | shell.update(); 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_simple_ESP32_server/Shellminator_simple_ESP32_server.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sept 10 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.09.30 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example shows a simple setup forShellminator. It will create an 11 | * interactive interface, but it does not execute any command. 12 | * See Shellminator_execute example for further information. 13 | */ 14 | 15 | #include 16 | #include "esp_wifi.h" 17 | 18 | #include "Shellminator.hpp" 19 | #include "Shellminator-IO.hpp" 20 | 21 | #define SERVER_PORT 23 22 | 23 | // WiFi credentials. 24 | const char* ssid = "your-wifi-ssid"; 25 | const char* password = "your-wifi-pass"; 26 | 27 | // Create an instance of the server. 28 | // It will be available on port 23. 29 | WiFiServer server( SERVER_PORT ); 30 | 31 | // Create a Shellminator object, and initialize it to use WiFiServer 32 | Shellminator shell( &server ); 33 | 34 | const char logo[] = 35 | 36 | " _____ __ ____ _ __ \r\n" 37 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 38 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 39 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 40 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 41 | "\r\n\033[0;37m" 42 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 43 | 44 | ; 45 | 46 | 47 | void setup() { 48 | 49 | // Initialize Serial with 115200 baudrate. 50 | Serial.begin( 115200 ); 51 | 52 | // If you using Atmega32U4, the code will wait, until 53 | // you open the serial port. 54 | while( !Serial ); 55 | 56 | // Clear the terminal 57 | shell.clear(); 58 | 59 | // Attach the logo. 60 | shell.attachLogo( logo ); 61 | 62 | // Print start message 63 | Serial.println( "Program begin..." ); 64 | 65 | // WiFi configuration section 66 | Serial.print( "Connect to WiFi: " ); 67 | Serial.print( ssid ); 68 | 69 | WiFi.mode( WIFI_STA ); 70 | WiFi.setSleep(WIFI_PS_NONE); 71 | WiFi.begin( ssid, password ); 72 | 73 | while( WiFi.status() != WL_CONNECTED ){ 74 | 75 | delay( 1000 ); 76 | Serial.print( "." ); 77 | 78 | } 79 | 80 | shell.beginServer(); 81 | 82 | Serial.println( " [ OK ]" ); 83 | 84 | Serial.println( "Connected!" ); 85 | Serial.print( "Device IP: " ); 86 | Serial.print( WiFi.localIP() ); 87 | Serial.print( " at port: " ); 88 | Serial.println( SERVER_PORT ); 89 | 90 | // initialize shell object. 91 | shell.begin( "arnold" ); 92 | 93 | } 94 | 95 | void loop() { 96 | 97 | shell.update(); 98 | 99 | } 100 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_simple_ESP32_websocket/Shellminator_simple_ESP32_websocket.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sept 10 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.09.30 8 | * 9 | * This example shows how to use the websocket implemenation of Shellminator. 10 | */ 11 | 12 | #include // <- https://github.com/Links2004/arduinoWebSockets 13 | #include 14 | #include 15 | #include 16 | #include "esp_wifi.h" 17 | 18 | #include "Shellminator.hpp" 19 | #include "Shellminator-Browser-Response.hpp" // <- It contains the webpage data 20 | 21 | // Websocket port for Shellminator. 22 | #define WEBSOCKET_PORT 81 23 | 24 | // Webserver port for webpage and contents. 25 | #define WEBSERVER_PORT 80 26 | 27 | // WiFi credentials. 28 | const char* ssid = "your-wifi-ssid"; 29 | const char* password = "your-wifi-pass"; 30 | 31 | // Create websocket object. 32 | WebSocketsServer webSocket = WebSocketsServer( WEBSOCKET_PORT ); 33 | 34 | // Create webserver object. 35 | WebServer server( WEBSERVER_PORT ); 36 | 37 | // Create a Shellminator object, and initialize it to use WebSocketsServer 38 | Shellminator shell( &webSocket ); 39 | 40 | // Shellminator logo. 41 | const char logo[] = 42 | 43 | " _____ __ ____ _ __ \r\n" 44 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 45 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 46 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 47 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 48 | "\r\n\033[0;37m" 49 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 50 | 51 | ; 52 | 53 | // This function generates a response for the index page. 54 | void handleIndex(){ 55 | server.send_P(200, "text/html", shellminator_html_response, shellminator_html_response_len ); 56 | } 57 | 58 | // This function generates a response for /xterm.js 59 | void handleXtermJs(){ 60 | server.send_P(200, "application/javascript", shellminator_xterm_js_response, shellminator_xterm_js_response_len ); 61 | } 62 | 63 | // This function generates a response for /xterm.css 64 | void handleXtermCss(){ 65 | server.send_P(200, "text/css", shellminator_xterm_css_response, shellminator_xterm_css_response_len ); 66 | } 67 | 68 | // This function generates a response for xterm-addon-web-links.js 69 | void handleXtermAddonWebLinks(){ 70 | server.send_P(200, "application/javascript", shellminator_xterm_addon_web_links_js_response, shellminator_xterm_addon_web_links_js_response_len ); 71 | } 72 | 73 | // This function generates a response for everything else. 74 | void handleNotFound() { 75 | String message = "File Not Found\n\n"; 76 | message += "URI: "; 77 | message += server.uri(); 78 | message += "\nMethod: "; 79 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 80 | message += "\nArguments: "; 81 | message += server.args(); 82 | message += "\n"; 83 | for (uint8_t i = 0; i < server.args(); i++) { 84 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 85 | } 86 | server.send(404, "text/plain", message); 87 | } 88 | 89 | // This function will be called on websocket event. 90 | void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { 91 | 92 | // This implementation only works with one websocket based terminal client. 93 | // Every websocket client get an identifier from 0 - 255. The num argument 94 | // is this identifier. We have to close every other client, than 0. 95 | if( num > 0 ){ 96 | 97 | // Send the problem to the client, than close it. 98 | webSocket.sendTXT( num, "\r\nNo more connections allowed!\r\n" ); 99 | webSocket.disconnect( num ); 100 | return; 101 | 102 | } 103 | 104 | // Depending on the event, we have to decide the next action. 105 | switch( type ){ 106 | 107 | // The client disconnected. 108 | case WStype_DISCONNECTED: 109 | 110 | // Currently, just print out the event. 111 | Serial.printf("[%u] Disconnected!\n", num); 112 | break; 113 | 114 | // New connection. 115 | case WStype_CONNECTED: 116 | 117 | // Clear the screen and print the logo with the banner. 118 | shell.clear(); 119 | shell.drawLogo(); 120 | shell.printBanner(); 121 | break; 122 | 123 | // Text data incoming 124 | case WStype_TEXT: 125 | 126 | // Print to serial, just for debug. 127 | Serial.printf("[%u] get Text: %s\n", num, payload); 128 | 129 | // In the Shellminator-IO files, the websocket channel 130 | // is implemented as a circular buffer. The incoming data 131 | // from the clients has to be pushed to this circular buffer 132 | // in the websocket event. 133 | shell.webSocketPush( payload, length ); 134 | break; 135 | 136 | } 137 | 138 | } 139 | 140 | void setup() { 141 | 142 | // Initialize Serial with 115200 baudrate. 143 | Serial.begin( 115200 ); 144 | 145 | // If you using Atmega32U4, the code will wait, until 146 | // you open the serial port. 147 | while( !Serial ); 148 | 149 | // Clear the terminal 150 | shell.clear(); 151 | 152 | // Attach the logo. 153 | shell.attachLogo( logo ); 154 | 155 | // Print start message 156 | Serial.println( "Program begin..." ); 157 | 158 | // WiFi configuration section 159 | Serial.print( "Connect to WiFi: " ); 160 | Serial.print( ssid ); 161 | 162 | WiFi.mode( WIFI_STA ); 163 | WiFi.setSleep(WIFI_PS_NONE); 164 | WiFi.begin( ssid, password ); 165 | 166 | while( WiFi.status() != WL_CONNECTED ){ 167 | 168 | delay( 1000 ); 169 | Serial.print( "." ); 170 | 171 | } 172 | 173 | Serial.println( " [ OK ]" ); 174 | 175 | Serial.println( "Connected!" ); 176 | Serial.print( "Device IP: " ); 177 | Serial.println( WiFi.localIP() ); 178 | 179 | // initialize shell object. 180 | shell.begin( "arnold" ); 181 | 182 | // Attach page handlers. 183 | server.on("/", handleIndex); 184 | server.on("/xterm.js", handleXtermJs); 185 | server.on("/xterm.css", handleXtermCss); 186 | server.on("/xterm-addon-web-links.js", handleXtermAddonWebLinks); 187 | server.onNotFound(handleNotFound); 188 | 189 | // Start webserver. 190 | server.begin(); 191 | 192 | // Start websocket server. 193 | webSocket.begin(); 194 | 195 | // Attach the webSocketEvent function to the websocket server. 196 | webSocket.onEvent(webSocketEvent); 197 | 198 | } 199 | 200 | void loop() { 201 | 202 | // Process everything. 203 | shell.update(); 204 | server.handleClient(); 205 | webSocket.loop(); 206 | 207 | // Give some time to the other tasks. 208 | delay( 2 ); 209 | 210 | } 211 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_simple_ESP8266_server/Shellminator_simple_ESP8266_server.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sept 10 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.09.30 8 | * 9 | * To test this example, you need a terminal emulator like PuTTY or Minicom. 10 | * This example shows a simple setup forShellminator. It will create an 11 | * interactive interface, but it does not execute any command. 12 | * See Shellminator_execute example for further information. 13 | */ 14 | 15 | #include 16 | 17 | #include "Shellminator.hpp" 18 | #include "Shellminator-IO.hpp" 19 | 20 | #define SERVER_PORT 23 21 | 22 | // WiFi credentials. 23 | const char* ssid = "your-wifi-ssid"; 24 | const char* password = "your-wifi-pass"; 25 | 26 | // Create an instance of the server. 27 | // It will be available on port 23. 28 | WiFiServer server( SERVER_PORT ); 29 | 30 | // Create a Shellminator object, and initialize it to use WiFiServer 31 | Shellminator shell( &server ); 32 | 33 | const char logo[] = 34 | 35 | " _____ __ ____ _ __ \r\n" 36 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 37 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 38 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 39 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 40 | "\r\n\033[0;37m" 41 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 42 | 43 | ; 44 | 45 | 46 | void setup() { 47 | 48 | // Initialize Serial with 115200 baudrate. 49 | Serial.begin( 115200 ); 50 | 51 | // If you using Atmega32U4, the code will wait, until 52 | // you open the serial port. 53 | while( !Serial ); 54 | 55 | // Clear the terminal 56 | shell.clear(); 57 | 58 | // Attach the logo. 59 | shell.attachLogo( logo ); 60 | 61 | // Print start message 62 | Serial.println( "Program begin..." ); 63 | 64 | // WiFi configuration section 65 | Serial.print( "Connect to WiFi: " ); 66 | Serial.print( ssid ); 67 | 68 | 69 | WiFi.mode( WIFI_STA ); 70 | WiFi.begin( ssid, password ); 71 | 72 | while( WiFi.status() != WL_CONNECTED ){ 73 | 74 | delay( 1000 ); 75 | Serial.print( "." ); 76 | 77 | } 78 | 79 | shell.beginServer(); 80 | 81 | Serial.println( " [ OK ]" ); 82 | 83 | Serial.println( "Connected!" ); 84 | Serial.print( "Device IP: " ); 85 | Serial.print( WiFi.localIP() ); 86 | Serial.print( " at port: " ); 87 | Serial.println( SERVER_PORT ); 88 | 89 | // initialize shell object. 90 | shell.begin( "arnold" ); 91 | 92 | } 93 | 94 | void loop() { 95 | 96 | shell.update(); 97 | 98 | } 99 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/examples/Shellminator_simple_ESP8266_websocket/Shellminator_simple_ESP8266_websocket.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Sept 10 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.09.30 8 | * 9 | * This example shows how to use the websocket implemenation of Shellminator. 10 | */ 11 | 12 | #include // <- https://github.com/Links2004/arduinoWebSockets 13 | #include 14 | #include 15 | #include 16 | 17 | #include "Shellminator.hpp" 18 | #include "Shellminator-Browser-Response.hpp" // <- It contains the webpage data 19 | 20 | // Websocket port for Shellminator. 21 | #define WEBSOCKET_PORT 81 22 | 23 | // Webserver port for webpage and contents. 24 | #define WEBSERVER_PORT 80 25 | 26 | // WiFi credentials. 27 | const char* ssid = "your-wifi-ssid"; 28 | const char* password = "your-wifi-pass"; 29 | 30 | // Create websocket object. 31 | WebSocketsServer webSocket = WebSocketsServer( WEBSOCKET_PORT ); 32 | 33 | // Create webserver object. 34 | ESP8266WebServer server(80); 35 | 36 | // Create a Shellminator object, and initialize it to use WebSocketsServer 37 | Shellminator shell( &webSocket ); 38 | 39 | // Shellminator logo. 40 | const char logo[] = 41 | 42 | " _____ __ ____ _ __ \r\n" 43 | " / ___// /_ ___ / / /___ ___ (_)___ ____ _/ /_____ _____\r\n" 44 | " \\__ \\/ __ \\/ _ \\/ / / __ `__ \\/ / __ \\/ __ `/ __/ __ \\/ ___/\r\n" 45 | " ___/ / / / / __/ / / / / / / / / / / / /_/ / /_/ /_/ / / \r\n" 46 | "/____/_/ /_/\\___/_/_/_/ /_/ /_/_/_/ /_/\\__,_/\\__/\\____/_/ \r\n" 47 | "\r\n\033[0;37m" 48 | "Visit on GitHub:\033[1;32m https://github.com/dani007200964/Shellminator\r\n\r\n" 49 | 50 | ; 51 | 52 | // This function generates a response for the index page. 53 | void handleIndex(){ 54 | server.send_P(200, "text/html", shellminator_html_response, shellminator_html_response_len ); 55 | } 56 | 57 | // This function generates a response for /xterm.js 58 | void handleXtermJs(){ 59 | server.send_P(200, "application/javascript", shellminator_xterm_js_response, shellminator_xterm_js_response_len ); 60 | } 61 | 62 | // This function generates a response for /xterm.css 63 | void handleXtermCss(){ 64 | server.send_P(200, "text/css", shellminator_xterm_css_response, shellminator_xterm_css_response_len ); 65 | } 66 | 67 | // This function generates a response for xterm-addon-web-links.js 68 | void handleXtermAddonWebLinks(){ 69 | server.send_P(200, "application/javascript", shellminator_xterm_addon_web_links_js_response, shellminator_xterm_addon_web_links_js_response_len ); 70 | } 71 | 72 | // This function generates a response for everything else. 73 | void handleNotFound() { 74 | String message = "File Not Found\n\n"; 75 | message += "URI: "; 76 | message += server.uri(); 77 | message += "\nMethod: "; 78 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 79 | message += "\nArguments: "; 80 | message += server.args(); 81 | message += "\n"; 82 | for (uint8_t i = 0; i < server.args(); i++) { 83 | message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; 84 | } 85 | server.send(404, "text/plain", message); 86 | } 87 | 88 | // This function will be called on websocket event. 89 | void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { 90 | 91 | // This implementation only works with one websocket based terminal client. 92 | // Every websocket client get an identifier from 0 - 255. The num argument 93 | // is this identifier. We have to close every other client, than 0. 94 | if( num > 0 ){ 95 | 96 | // Send the problem to the client, than close it. 97 | webSocket.sendTXT( num, "\r\nNo more connections allowed!\r\n" ); 98 | webSocket.disconnect( num ); 99 | return; 100 | 101 | } 102 | 103 | // Depending on the event, we have to decide the next action. 104 | switch( type ){ 105 | 106 | // The client disconnected. 107 | case WStype_DISCONNECTED: 108 | 109 | // Currently, just print out the event. 110 | Serial.printf("[%u] Disconnected!\n", num); 111 | break; 112 | 113 | // New connection. 114 | case WStype_CONNECTED: 115 | 116 | // Clear the screen and print the logo with the banner. 117 | shell.clear(); 118 | shell.begin( "arnold" ); 119 | break; 120 | 121 | // Text data incoming 122 | case WStype_TEXT: 123 | 124 | // Print to serial, just for debug. 125 | Serial.printf("[%u] get Text: %s\n", num, payload); 126 | 127 | // In the Shellminator-IO files, the websocket channel 128 | // is implemented as a circular buffer. The incoming data 129 | // from the clients has to be pushed to this circular buffer 130 | // in the websocket event. 131 | shell.webSocketPush( payload, length ); 132 | break; 133 | 134 | } 135 | 136 | } 137 | 138 | void setup() { 139 | 140 | // Initialize Serial with 115200 baudrate. 141 | Serial.begin( 115200 ); 142 | 143 | // If you using Atmega32U4, the code will wait, until 144 | // you open the serial port. 145 | while( !Serial ); 146 | 147 | // Clear the terminal 148 | shell.clear(); 149 | 150 | // Attach the logo. 151 | shell.attachLogo( logo ); 152 | 153 | // Print start message 154 | Serial.println( "Program begin..." ); 155 | 156 | // WiFi configuration section 157 | Serial.print( "Connect to WiFi: " ); 158 | Serial.print( ssid ); 159 | 160 | WiFi.mode( WIFI_STA ); 161 | WiFi.begin( ssid, password ); 162 | 163 | while( WiFi.status() != WL_CONNECTED ){ 164 | 165 | delay( 1000 ); 166 | Serial.print( "." ); 167 | 168 | } 169 | 170 | Serial.println( " [ OK ]" ); 171 | 172 | Serial.println( "Connected!" ); 173 | Serial.print( "Device IP: " ); 174 | Serial.print( WiFi.localIP() ); 175 | Serial.print( " at port: " ); 176 | Serial.println( WEBSOCKET_PORT ); 177 | 178 | // initialize shell object. 179 | shell.begin( "arnold" ); 180 | 181 | // Index page handler. 182 | server.on("/", handleIndex); 183 | 184 | // xterm.js handler. 185 | server.on("/xterm.js", handleXtermJs); 186 | 187 | // xterm.css handler. 188 | server.on("/xterm.css", handleXtermCss); 189 | 190 | server.on("/xterm-addon-web-links.js", handleXtermAddonWebLinks); 191 | 192 | // Not found handler. 193 | server.onNotFound(handleNotFound); 194 | 195 | // Start webserver. 196 | server.begin(); 197 | 198 | // Start websocket server. 199 | webSocket.begin(); 200 | 201 | // Attach the webSocketEvent function to the websocket server. 202 | webSocket.onEvent(webSocketEvent); 203 | 204 | } 205 | 206 | void loop() { 207 | 208 | shell.update(); 209 | server.handleClient(); 210 | webSocket.loop(); 211 | 212 | } 213 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Colors for Commander-API 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | Shellminator KEYWORD1 8 | shellminatorChannel KEYWORD1 9 | shellminatorArduinoSerialChannel KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions 13 | ####################################### 14 | 15 | addExecFunc KEYWORD2 16 | begin KEYWORD2 17 | sendBackspace KEYWORD2 18 | clear KEYWORD2 19 | update KEYWORD2 20 | setTerminalCharacterColor KEYWORD2 21 | drawLogo KEYWORD2 22 | setBannerText KEYWORD2 23 | attachLogo KEYWORD2 24 | overrideUpArrow KEYWORD2 25 | overrideDownArrow KEYWORD2 26 | overrideLeftArrow KEYWORD2 27 | overrideRightArrow KEYWORD2 28 | overrideAbortKey KEYWORD2 29 | freeUpArrow KEYWORD2 30 | freeDownArrow KEYWORD2 31 | freeLeftArrow KEYWORD2 32 | freeRightArrow KEYWORD2 33 | freeAbortKey KEYWORD2 34 | attachCommander KEYWORD2 35 | 36 | ####################################### 37 | # Structures 38 | ####################################### 39 | 40 | API_t KEYWORD2 41 | 42 | ####################################### 43 | # Constants 44 | ####################################### 45 | 46 | COMMANDER_MAX_COMMAND_SIZE LITERAL1 47 | COMMAND_PRINTF_BUFF_LEN LITERAL1 48 | version LITERAL1 49 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/library.properties: -------------------------------------------------------------------------------- 1 | name=Shellminator 2 | version=1.2.0 3 | author=Daniel Hajnal 4 | maintainer=Daniel Hajnal 5 | sentence=This library can be used to interface with VT-100 compatible terminal emulators. 6 | paragraph=It acts like a reduced functionality terminal interface. 7 | category=Communication 8 | url=https://github.com/dani007200964/Shellminator 9 | architectures=* 10 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/LICENSE-xterm: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) 2 | Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) 3 | Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/LICENSE-xterm-web-links: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 36 | 38 | 42 | 46 | 54 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radio3-network/B3OS/c7ba3083391eb68af87f0c8bd805043c102536cc/lib/Shellminator_dev/other/xterm_based_webpage/favicon.ico -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/minifiedPage/xterm-addon-web-links.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WebLinksAddon=t():e.WebLinksAddon=t()}(self,(function(){return(()=>{"use strict";var e={6:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkComputer=t.WebLinkProvider=void 0,t.WebLinkProvider=class{constructor(e,t,o,n={}){this._terminal=e,this._regex=t,this._handler=o,this._options=n}provideLinks(e,t){const n=o.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(n))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,o)=>{if(this._options.hover){const{range:n}=e;this._options.hover(t,o,n)}},e)))}};class o{static computeLink(e,t,n,i){const r=new RegExp(t.source,(t.flags||"")+"g"),[s,a]=o._translateBufferLineToStringWithWrap(e-1,!1,n);let l,c=-1;const d=[];for(;null!==(l=r.exec(s));){const e=l[1];if(!e){console.log("match found without corresponding matchIndex");break}if(c=s.indexOf(e,c+1),r.lastIndex=c+e.length,c<0)break;let t=c+e.length,o=a+1;for(;t>n.cols;)t-=n.cols,o++;let p=c+1,h=a+1;for(;p>n.cols;)p-=n.cols,h++;const u={start:{x:p,y:h},end:{x:t,y:o}};d.push({range:u,text:e,activate:i})}return d}static _translateBufferLineToStringWithWrap(e,t,o){let n,i,r="";do{const t=o.buffer.active.getLine(e);if(!t)break;t.isWrapped&&e--,i=t.isWrapped}while(i);const s=e;do{const i=o.buffer.active.getLine(e+1);n=!!i&&i.isWrapped;const s=o.buffer.active.getLine(e);if(!s)break;r+=s.translateToString(!n&&t).substring(0,o.cols),e++}while(n);return[r,s]}}t.LinkComputer=o}},t={};function o(n){var i=t[n];if(void 0!==i)return i.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,o),r.exports}var n={};return(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.WebLinksAddon=void 0;const t=o(6),i=new RegExp("(?:^|[^\\da-z\\.-]+)((https?:\\/\\/)((([\\da-z\\.-]+)\\.([a-z\\.]{2,18}))|((\\d{1,3}\\.){3}\\d{1,3})|(localhost))(:\\d{1,5})?((\\/[\\/\\w\\.\\-%~:+@]*)*([^:\"'\\s]))?(\\?[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&'*+,:;~\\=\\.\\-]*)?(#[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&'*+,:;~\\=\\.\\-]*)?)($|[^\\/\\w\\.\\-%]+)");function r(e,t){const o=window.open();if(o){try{o.opener=null}catch(e){}o.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}e.WebLinksAddon=class{constructor(e=r,t={}){this._handler=e,this._options=t}activate(e){this._terminal=e;const o=this._options,n=o.urlRegex||i;this._linkProvider=this._terminal.registerLinkProvider(new t.WebLinkProvider(this._terminal,n,this._handler,o))}dispose(){var e;null===(e=this._linkProvider)||void 0===e||e.dispose()}}})(),n})()})); 2 | //# sourceMappingURL=xterm-addon-web-links.js.map -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/minifiedPage/xterm.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 The xterm.js authors. All rights reserved. 3 | * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 4 | * https://github.com/chjj/term.js 5 | * @license MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | * Originally forked from (with the author's permission): 26 | * Fabrice Bellard's javascript vt100 for jslinux: 27 | * http://bellard.org/jslinux/ 28 | * Copyright (c) 2011 Fabrice Bellard 29 | * The original design remains. The terminal itself 30 | * has been extended to include xterm CSI codes, among 31 | * other features. 32 | */ 33 | 34 | /** 35 | * Default styles for xterm.js 36 | */ 37 | 38 | .xterm { 39 | cursor: text; 40 | position: relative; 41 | user-select: none; 42 | -ms-user-select: none; 43 | -webkit-user-select: none; 44 | } 45 | 46 | .xterm.focus, 47 | .xterm:focus { 48 | outline: none; 49 | } 50 | 51 | .xterm .xterm-helpers { 52 | position: absolute; 53 | top: 0; 54 | /** 55 | * The z-index of the helpers must be higher than the canvases in order for 56 | * IMEs to appear on top. 57 | */ 58 | z-index: 5; 59 | } 60 | 61 | .xterm .xterm-helper-textarea { 62 | padding: 0; 63 | border: 0; 64 | margin: 0; 65 | /* Move textarea out of the screen to the far left, so that the cursor is not visible */ 66 | position: absolute; 67 | opacity: 0; 68 | left: -9999em; 69 | top: 0; 70 | width: 0; 71 | height: 0; 72 | z-index: -5; 73 | /** Prevent wrapping so the IME appears against the textarea at the correct position */ 74 | white-space: nowrap; 75 | overflow: hidden; 76 | resize: none; 77 | } 78 | 79 | .xterm .composition-view { 80 | /* TODO: Composition position got messed up somewhere */ 81 | background: #000; 82 | color: #FFF; 83 | display: none; 84 | position: absolute; 85 | white-space: nowrap; 86 | z-index: 1; 87 | } 88 | 89 | .xterm .composition-view.active { 90 | display: block; 91 | } 92 | 93 | .xterm .xterm-viewport { 94 | /* On OS X this is required in order for the scroll bar to appear fully opaque */ 95 | background-color: #000; 96 | overflow-y: scroll; 97 | cursor: default; 98 | position: absolute; 99 | right: 0; 100 | left: 0; 101 | top: 0; 102 | bottom: 0; 103 | } 104 | 105 | .xterm .xterm-screen { 106 | position: relative; 107 | } 108 | 109 | .xterm .xterm-screen canvas { 110 | position: absolute; 111 | left: 0; 112 | top: 0; 113 | } 114 | 115 | .xterm .xterm-scroll-area { 116 | visibility: hidden; 117 | } 118 | 119 | .xterm-char-measure-element { 120 | display: inline-block; 121 | visibility: hidden; 122 | position: absolute; 123 | top: 0; 124 | left: -9999em; 125 | line-height: normal; 126 | } 127 | 128 | .xterm.enable-mouse-events { 129 | /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ 130 | cursor: default; 131 | } 132 | 133 | .xterm.xterm-cursor-pointer, 134 | .xterm .xterm-cursor-pointer { 135 | cursor: pointer; 136 | } 137 | 138 | .xterm.column-select.focus { 139 | /* Column selection mode */ 140 | cursor: crosshair; 141 | } 142 | 143 | .xterm .xterm-accessibility, 144 | .xterm .xterm-message { 145 | position: absolute; 146 | left: 0; 147 | top: 0; 148 | bottom: 0; 149 | right: 0; 150 | z-index: 10; 151 | color: transparent; 152 | } 153 | 154 | .xterm .live-region { 155 | position: absolute; 156 | left: -9999px; 157 | width: 1px; 158 | height: 1px; 159 | overflow: hidden; 160 | } 161 | 162 | .xterm-dim { 163 | opacity: 0.5; 164 | } 165 | 166 | .xterm-underline { 167 | text-decoration: underline; 168 | } 169 | 170 | .xterm-strikethrough { 171 | text-decoration: line-through; 172 | } 173 | 174 | .xterm-screen .xterm-decoration-container .xterm-decoration { 175 | z-index: 6; 176 | position: absolute; 177 | } 178 | 179 | .xterm-decoration-overview-ruler { 180 | z-index: 7; 181 | position: absolute; 182 | top: 0; 183 | right: 0; 184 | pointer-events: none; 185 | } 186 | 187 | .xterm-decoration-top { 188 | z-index: 2; 189 | position: relative; 190 | } 191 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/ws_server.py: -------------------------------------------------------------------------------- 1 | """ 2 | * Created on Aug 12 2022 3 | * 4 | * Copyright (c) 2022 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.009.12 8 | 9 | MIT License 10 | 11 | Copyright (c) 2020 Daniel Hajnal 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | """ 31 | 32 | import asyncio 33 | 34 | import websockets 35 | 36 | # create handler for each connection 37 | 38 | async def handler(websocket, path): 39 | 40 | while True: 41 | message = await websocket.recv() 42 | await websocket.send( message ) 43 | print( message ) 44 | 45 | 46 | 47 | start_server = websockets.serve(handler, "localhost", 3000) 48 | 49 | 50 | 51 | asyncio.get_event_loop().run_until_complete(start_server) 52 | 53 | asyncio.get_event_loop().run_forever() 54 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/xterm-addon-web-links.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WebLinksAddon=t():e.WebLinksAddon=t()}(self,(function(){return(()=>{"use strict";var e={6:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkComputer=t.WebLinkProvider=void 0,t.WebLinkProvider=class{constructor(e,t,o,n={}){this._terminal=e,this._regex=t,this._handler=o,this._options=n}provideLinks(e,t){const n=o.computeLink(e,this._regex,this._terminal,this._handler);t(this._addCallbacks(n))}_addCallbacks(e){return e.map((e=>(e.leave=this._options.leave,e.hover=(t,o)=>{if(this._options.hover){const{range:n}=e;this._options.hover(t,o,n)}},e)))}};class o{static computeLink(e,t,n,i){const r=new RegExp(t.source,(t.flags||"")+"g"),[s,a]=o._translateBufferLineToStringWithWrap(e-1,!1,n);let l,c=-1;const d=[];for(;null!==(l=r.exec(s));){const e=l[1];if(!e){console.log("match found without corresponding matchIndex");break}if(c=s.indexOf(e,c+1),r.lastIndex=c+e.length,c<0)break;let t=c+e.length,o=a+1;for(;t>n.cols;)t-=n.cols,o++;let p=c+1,h=a+1;for(;p>n.cols;)p-=n.cols,h++;const u={start:{x:p,y:h},end:{x:t,y:o}};d.push({range:u,text:e,activate:i})}return d}static _translateBufferLineToStringWithWrap(e,t,o){let n,i,r="";do{const t=o.buffer.active.getLine(e);if(!t)break;t.isWrapped&&e--,i=t.isWrapped}while(i);const s=e;do{const i=o.buffer.active.getLine(e+1);n=!!i&&i.isWrapped;const s=o.buffer.active.getLine(e);if(!s)break;r+=s.translateToString(!n&&t).substring(0,o.cols),e++}while(n);return[r,s]}}t.LinkComputer=o}},t={};function o(n){var i=t[n];if(void 0!==i)return i.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,o),r.exports}var n={};return(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.WebLinksAddon=void 0;const t=o(6),i=new RegExp("(?:^|[^\\da-z\\.-]+)((https?:\\/\\/)((([\\da-z\\.-]+)\\.([a-z\\.]{2,18}))|((\\d{1,3}\\.){3}\\d{1,3})|(localhost))(:\\d{1,5})?((\\/[\\/\\w\\.\\-%~:+@]*)*([^:\"'\\s]))?(\\?[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&'*+,:;~\\=\\.\\-]*)?(#[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&'*+,:;~\\=\\.\\-]*)?)($|[^\\/\\w\\.\\-%]+)");function r(e,t){const o=window.open();if(o){try{o.opener=null}catch(e){}o.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}e.WebLinksAddon=class{constructor(e=r,t={}){this._handler=e,this._options=t}activate(e){this._terminal=e;const o=this._options,n=o.urlRegex||i;this._linkProvider=this._terminal.registerLinkProvider(new t.WebLinkProvider(this._terminal,n,this._handler,o))}dispose(){var e;null===(e=this._linkProvider)||void 0===e||e.dispose()}}})(),n})()})); 2 | //# sourceMappingURL=xterm-addon-web-links.js.map -------------------------------------------------------------------------------- /lib/Shellminator_dev/other/xterm_based_webpage/xterm.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 The xterm.js authors. All rights reserved. 3 | * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 4 | * https://github.com/chjj/term.js 5 | * @license MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | * Originally forked from (with the author's permission): 26 | * Fabrice Bellard's javascript vt100 for jslinux: 27 | * http://bellard.org/jslinux/ 28 | * Copyright (c) 2011 Fabrice Bellard 29 | * The original design remains. The terminal itself 30 | * has been extended to include xterm CSI codes, among 31 | * other features. 32 | */ 33 | 34 | /** 35 | * Default styles for xterm.js 36 | */ 37 | 38 | .xterm { 39 | cursor: text; 40 | position: relative; 41 | user-select: none; 42 | -ms-user-select: none; 43 | -webkit-user-select: none; 44 | } 45 | 46 | .xterm.focus, 47 | .xterm:focus { 48 | outline: none; 49 | } 50 | 51 | .xterm .xterm-helpers { 52 | position: absolute; 53 | top: 0; 54 | /** 55 | * The z-index of the helpers must be higher than the canvases in order for 56 | * IMEs to appear on top. 57 | */ 58 | z-index: 5; 59 | } 60 | 61 | .xterm .xterm-helper-textarea { 62 | padding: 0; 63 | border: 0; 64 | margin: 0; 65 | /* Move textarea out of the screen to the far left, so that the cursor is not visible */ 66 | position: absolute; 67 | opacity: 0; 68 | left: -9999em; 69 | top: 0; 70 | width: 0; 71 | height: 0; 72 | z-index: -5; 73 | /** Prevent wrapping so the IME appears against the textarea at the correct position */ 74 | white-space: nowrap; 75 | overflow: hidden; 76 | resize: none; 77 | } 78 | 79 | .xterm .composition-view { 80 | /* TODO: Composition position got messed up somewhere */ 81 | background: #000; 82 | color: #FFF; 83 | display: none; 84 | position: absolute; 85 | white-space: nowrap; 86 | z-index: 1; 87 | } 88 | 89 | .xterm .composition-view.active { 90 | display: block; 91 | } 92 | 93 | .xterm .xterm-viewport { 94 | /* On OS X this is required in order for the scroll bar to appear fully opaque */ 95 | background-color: #000; 96 | overflow-y: scroll; 97 | cursor: default; 98 | position: absolute; 99 | right: 0; 100 | left: 0; 101 | top: 0; 102 | bottom: 0; 103 | } 104 | 105 | .xterm .xterm-screen { 106 | position: relative; 107 | } 108 | 109 | .xterm .xterm-screen canvas { 110 | position: absolute; 111 | left: 0; 112 | top: 0; 113 | } 114 | 115 | .xterm .xterm-scroll-area { 116 | visibility: hidden; 117 | } 118 | 119 | .xterm-char-measure-element { 120 | display: inline-block; 121 | visibility: hidden; 122 | position: absolute; 123 | top: 0; 124 | left: -9999em; 125 | line-height: normal; 126 | } 127 | 128 | .xterm.enable-mouse-events { 129 | /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ 130 | cursor: default; 131 | } 132 | 133 | .xterm.xterm-cursor-pointer, 134 | .xterm .xterm-cursor-pointer { 135 | cursor: pointer; 136 | } 137 | 138 | .xterm.column-select.focus { 139 | /* Column selection mode */ 140 | cursor: crosshair; 141 | } 142 | 143 | .xterm .xterm-accessibility, 144 | .xterm .xterm-message { 145 | position: absolute; 146 | left: 0; 147 | top: 0; 148 | bottom: 0; 149 | right: 0; 150 | z-index: 10; 151 | color: transparent; 152 | } 153 | 154 | .xterm .live-region { 155 | position: absolute; 156 | left: -9999px; 157 | width: 1px; 158 | height: 1px; 159 | overflow: hidden; 160 | } 161 | 162 | .xterm-dim { 163 | opacity: 0.5; 164 | } 165 | 166 | .xterm-underline { 167 | text-decoration: underline; 168 | } 169 | 170 | .xterm-strikethrough { 171 | text-decoration: line-through; 172 | } 173 | 174 | .xterm-screen .xterm-decoration-container .xterm-decoration { 175 | z-index: 6; 176 | position: absolute; 177 | } 178 | 179 | .xterm-decoration-overview-ruler { 180 | z-index: 7; 181 | position: absolute; 182 | top: 0; 183 | right: 0; 184 | pointer-events: none; 185 | } 186 | 187 | .xterm-decoration-top { 188 | z-index: 2; 189 | position: relative; 190 | } 191 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/src/Shellminator-IO.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | */ 9 | 10 | /* 11 | MIT License 12 | 13 | Copyright (c) 2020 Daniel Hajnal 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | #include "Shellminator-IO.hpp" 35 | 36 | 37 | #ifdef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 38 | 39 | //----- Response for WebSocket Server class -----// 40 | 41 | void shellminatorWebSocketChannel::select( WebSocketsServer *server_p, int8_t clientID_p ){ 42 | 43 | server = server_p; 44 | clientID = clientID_p; 45 | 46 | } 47 | 48 | void shellminatorWebSocketChannel::push( uint8_t data ){ 49 | 50 | buffer[ writePointer ] = data; 51 | writePointer++; 52 | if( writePointer >= SHELLMINATOR_WEBSOCKET_BUFFER_LEN ){ 53 | writePointer = 0; 54 | } 55 | 56 | } 57 | 58 | void shellminatorWebSocketChannel::push( uint8_t* data, size_t size ){ 59 | 60 | uint32_t i; 61 | 62 | for( i = 0; i < size; i++ ){ 63 | 64 | buffer[ writePointer ] = data[ i ]; 65 | writePointer++; 66 | if( writePointer >= SHELLMINATOR_WEBSOCKET_BUFFER_LEN ){ 67 | writePointer = 0; 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | int shellminatorWebSocketChannel::available(){ 75 | 76 | if( writePointer == readPointer ){ 77 | return 0; 78 | } 79 | 80 | else if( writePointer > readPointer ){ 81 | return writePointer - readPointer; 82 | } 83 | 84 | else{ 85 | 86 | return SHELLMINATOR_WEBSOCKET_BUFFER_LEN - readPointer + writePointer; 87 | 88 | } 89 | 90 | } 91 | 92 | int shellminatorWebSocketChannel::read(){ 93 | 94 | int ret; 95 | 96 | if( writePointer == readPointer ){ 97 | 98 | return -1; 99 | 100 | } 101 | 102 | else{ 103 | 104 | ret = (uint8_t)buffer[ readPointer ]; 105 | readPointer++; 106 | 107 | if( readPointer >= SHELLMINATOR_WEBSOCKET_BUFFER_LEN ){ 108 | readPointer = 0; 109 | } 110 | 111 | } 112 | 113 | return ret; 114 | 115 | } 116 | 117 | int shellminatorWebSocketChannel::peek(){ 118 | 119 | if( writePointer == readPointer ){ 120 | 121 | return -1; 122 | 123 | } 124 | 125 | else{ 126 | 127 | return (uint8_t)buffer[ readPointer ]; 128 | 129 | } 130 | 131 | } 132 | 133 | void shellminatorWebSocketChannel::flush(){ 134 | 135 | // Todo Maybe clear the input buffer? 136 | 137 | } 138 | 139 | size_t shellminatorWebSocketChannel::write( uint8_t b ){ 140 | 141 | if( server ){ 142 | server -> sendTXT( clientID, &b, 1 ); 143 | return 1; 144 | } 145 | return 0; 146 | 147 | } 148 | 149 | size_t shellminatorWebSocketChannel::write( const uint8_t *buffer, size_t size ){ 150 | 151 | if( server ){ 152 | server -> sendTXT( clientID, buffer, size ); 153 | return 1; 154 | } 155 | return 0; 156 | 157 | } 158 | 159 | //---- print section ----// 160 | 161 | size_t shellminatorWebSocketChannel::print( char c ){ 162 | 163 | if( server ){ 164 | server -> sendTXT( clientID, (uint8_t*)&c, 1 ); 165 | return 1; 166 | } 167 | return 0; 168 | 169 | } 170 | 171 | size_t shellminatorWebSocketChannel::print( uint8_t b ){ 172 | 173 | char outBuff[10]; 174 | uint32_t dataSize; 175 | 176 | snprintf( outBuff, 10, "%u", (int)b ); 177 | 178 | dataSize = strlen( outBuff ); 179 | 180 | if( server ){ 181 | server -> sendTXT( clientID, (uint8_t*)outBuff, dataSize ); 182 | return dataSize; 183 | } 184 | return 0; 185 | 186 | } 187 | 188 | size_t shellminatorWebSocketChannel::print( char *str ){ 189 | 190 | uint32_t dataSize; 191 | 192 | dataSize = strlen( str ); 193 | 194 | if( server ){ 195 | server -> sendTXT( clientID, (uint8_t*)str, dataSize ); 196 | return dataSize; 197 | } 198 | 199 | return 0; 200 | 201 | } 202 | 203 | size_t shellminatorWebSocketChannel::print( const char *str ){ 204 | 205 | uint32_t dataSize; 206 | 207 | dataSize = strlen( str ); 208 | 209 | if( server ){ 210 | server -> sendTXT( clientID, (uint8_t*)str, dataSize ); 211 | return dataSize; 212 | } 213 | return 0; 214 | 215 | } 216 | 217 | int8_t shellminatorWebSocketChannel::getClientID(){ 218 | 219 | return clientID; 220 | 221 | } 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/src/Shellminator-Settings.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Aug 10 2020 3 | * 4 | * Copyright (c) 2020 - Daniel Hajnal 5 | * hajnal.daniel96@gmail.com 6 | * This file is part of the Shellminator project. 7 | * Modified 2022.05.08 8 | */ 9 | 10 | /* 11 | MIT License 12 | 13 | Copyright (c) 2020 Daniel Hajnal 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | */ 33 | 34 | #ifndef SHELLMINATOR_SETTINGS_HPP_ 35 | #define SHELLMINATOR_SETTINGS_HPP_ 36 | 37 | #ifdef ESP32 38 | 39 | #ifndef SHELLMINATOR_USE_WIFI_CLIENT 40 | #define SHELLMINATOR_USE_WIFI_CLIENT 41 | #endif 42 | 43 | #ifndef SHELLMINATOR_ENABLE_SEARCH_MODULE 44 | #define SHELLMINATOR_ENABLE_SEARCH_MODULE 45 | #endif 46 | 47 | #ifndef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE 48 | #define SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE 49 | #endif 50 | 51 | #ifndef SHELLMINATOR_ENABLE_QR_SUPPORT 52 | #define SHELLMINATOR_ENABLE_QR_SUPPORT 53 | #endif 54 | 55 | /* NOT WORKING YET! 56 | #ifndef SHELLMINATOR_ENABLE_PASSWORD_MODULE 57 | #define SHELLMINATOR_ENABLE_PASSWORD_MODULE 58 | #endif 59 | */ 60 | 61 | #ifndef SHELLMINATOR_BUFF_LEN 62 | #define SHELLMINATOR_BUFF_LEN 250 63 | #endif 64 | 65 | #ifndef SHELLMINATOR_BUFF_DIM 66 | #define SHELLMINATOR_BUFF_DIM 5 67 | #endif 68 | 69 | #ifndef SHELLMINATOR_BANNER_LEN 70 | #define SHELLMINATOR_BANNER_LEN 20 71 | #endif 72 | 73 | #ifndef SHELLMINATOR_BANNER_PATH_LEN 74 | #define SHELLMINATOR_BANNER_PATH_LEN 20 75 | #endif 76 | 77 | #ifdef __has_include 78 | #if __has_include () 79 | #ifndef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 80 | #define SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 81 | #endif 82 | #endif 83 | #endif 84 | 85 | #endif 86 | 87 | #ifdef ESP8266 88 | 89 | #ifndef SHELLMINATOR_USE_WIFI_CLIENT 90 | #define SHELLMINATOR_USE_WIFI_CLIENT 91 | #endif 92 | 93 | #ifndef SHELLMINATOR_ENABLE_SEARCH_MODULE 94 | #define SHELLMINATOR_ENABLE_SEARCH_MODULE 95 | #endif 96 | 97 | #ifndef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE 98 | #define SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE 99 | #endif 100 | 101 | #ifndef SHELLMINATOR_ENABLE_QR_SUPPORT 102 | #define SHELLMINATOR_ENABLE_QR_SUPPORT 103 | #endif 104 | 105 | /* NOT WORKING YET! 106 | #ifndef SHELLMINATOR_ENABLE_PASSWORD_MODULE 107 | #define SHELLMINATOR_ENABLE_PASSWORD_MODULE 108 | #endif 109 | */ 110 | 111 | #ifndef SHELLMINATOR_BUFF_LEN 112 | #define SHELLMINATOR_BUFF_LEN 50 113 | #endif 114 | 115 | #ifndef SHELLMINATOR_BUFF_DIM 116 | #define SHELLMINATOR_BUFF_DIM 20 117 | #endif 118 | 119 | #ifndef SHELLMINATOR_BANNER_LEN 120 | #define SHELLMINATOR_BANNER_LEN 20 121 | #endif 122 | 123 | #ifndef SHELLMINATOR_BANNER_PATH_LEN 124 | #define SHELLMINATOR_BANNER_PATH_LEN 20 125 | #endif 126 | 127 | #ifdef __has_include 128 | #if __has_include () 129 | #ifndef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 130 | #define SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 131 | #endif 132 | #endif 133 | #endif 134 | 135 | #endif 136 | 137 | /// If defined, enables high memory mode. 138 | /// 139 | /// If your microcontroller has relativley high amount of RAM memory, 140 | /// I highly recommend to use it. 141 | /// @note This macro has to be defined befor importing the Shellminator.hpp. If not then the default value will be BOLD. 142 | #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE 143 | #define SHELLMINATOR_ACCELERATOR_BUFFER_LEN SHELLMINATOR_BANNER_LEN + SHELLMINATOR_BANNER_PATH_LEN + SHELLMINATOR_BUFF_LEN * 2 + 30 144 | #endif 145 | 146 | /// Definition of the maximum length of each command 147 | #ifndef SHELLMINATOR_BUFF_LEN 148 | #define SHELLMINATOR_BUFF_LEN 20 149 | #endif 150 | 151 | /// Definition of the maximum length of the previous command memory 152 | /// @warning Be careful with the The value of this definition. If it is to high your RAM will be eaten! 153 | /// @note The total amount of RAM consumed by the object in bytes can be calculated as: \link SHELLMINATOR_BUFF_LEN \endlink * \link SHELLMINATOR_BUFF_DIM \endlink 154 | #ifndef SHELLMINATOR_BUFF_DIM 155 | #define SHELLMINATOR_BUFF_DIM 5 156 | #endif 157 | 158 | /// Maximum length of the banner text 159 | #ifndef SHELLMINATOR_BANNER_LEN 160 | #define SHELLMINATOR_BANNER_LEN 10 161 | #endif 162 | 163 | #ifndef SHELLMINATOR_BANNER_PATH_LEN 164 | #define SHELLMINATOR_BANNER_PATH_LEN 10 165 | #endif 166 | 167 | /// Color of the startup logo 168 | /// @note This macro has to be defined befor importing the Shellminator.hpp. If not then the default value will be RED. 169 | #ifndef SHELLMINATOR_LOGO_FONT_STYLE 170 | #define SHELLMINATOR_LOGO_FONT_STYLE BOLD 171 | #endif 172 | 173 | /// Style of the startup logo 174 | /// @note This macro has to be defined befor importing the Shellminator.hpp. If not then the default value will be BOLD. 175 | #ifndef SHELLMINATOR_LOGO_COLOR 176 | #define SHELLMINATOR_LOGO_COLOR RED 177 | #endif 178 | 179 | /// If defined, enables the WebSocket Module. 180 | /// @note This macro has to be defined befor importing the Shellminator.hpp. If not then the default value will be BOLD. 181 | #ifdef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE 182 | #define SHELLMINATOR_WEBSOCKET_BUFFER_LEN 50 183 | #endif 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /lib/Shellminator_dev/src/external/nayuki-qrcodegen/license.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Project Nayuki. (MIT License) 2 | https://www.nayuki.io/page/qr-code-generator-library 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in the 6 | Software without restriction, including without limitation the rights to use, copy, 7 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 8 | and to permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | * The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | * The Software is provided "as is", without warranty of any kind, express or implied, 15 | including but not limited to the warranties of merchantability, fitness for a particular 16 | purpose and noninfringement. In no event shall the authors or copyright holders be liable 17 | for any claim, damages or other liability, whether in an action of contract, tort or 18 | otherwise, arising from, out of or in connection with the Software or the use or other 19 | dealings in the Software. 20 | -------------------------------------------------------------------------------- /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 | ; Old libs that can be used in the future: 12 | ; henrikste/ESP32FTPServer@^0.0.2 13 | 14 | 15 | [env:esp32dev] 16 | platform = espressif32@5.2.0 17 | board = esp32dev 18 | framework = arduino 19 | monitor_speed = 115200 20 | build_flags = 21 | -Ofast 22 | -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE 23 | -D LV_CONF_PATH=${platformio.include_dir}/lv_conf.h 24 | -D TFT_ORIENTATION_LANDSCAPE 25 | -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC 26 | lib_deps = 27 | SPI@^2.0.0 28 | Wire@^2.0.0 29 | WiFi@^2.0.0 30 | lvgl/lvgl@^8.3.4 31 | dani007200964/Commander-API@^2.1.0 32 | adafruit/SdFat - Adafruit Fork@^2.2.1 33 | henrikste/ESP32FTPServer@^0.0.2 34 | -------------------------------------------------------------------------------- /src/apps/blum_app_home.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "core/blum_global.h" 4 | #include "core/blum_widgets.h" 5 | #include "core/blum_status_bar.h" 6 | #include "core/blum_navigation.h" 7 | 8 | 9 | static void btn_event_cb(lv_event_t *e) 10 | { 11 | 12 | auto code = lv_event_get_code(e); 13 | auto btn = lv_event_get_target(e); 14 | if (code == LV_EVENT_CLICKED) 15 | { 16 | static uint8_t cnt = 0; 17 | cnt++; 18 | //SerialWriteLn("Pressed"); 19 | //logWrite("Pressed"); 20 | 21 | auto label = lv_obj_get_child(btn, 0); 22 | lv_label_set_text_fmt(label, "Button: %d", cnt); 23 | } 24 | } 25 | 26 | 27 | 28 | static void createWindowHome(){ 29 | Serial.println("Create window: Home"); 30 | 31 | // delete the memory map 32 | HashMapClear(mapWindows); 33 | // restore the icon and index 34 | navClean(); 35 | // create a usable window to place components 36 | homeWindow = createWindow("Home"); 37 | 38 | // add some testing info 39 | auto btnDebug = lv_btn_create(homeWindow); 40 | lv_obj_set_pos(btnDebug, 10, 10); 41 | lv_obj_set_size(btnDebug, 120, 50); 42 | lv_obj_align(btnDebug, LV_ALIGN_CENTER, 0, 0); 43 | lv_obj_add_event_cb(btnDebug, btn_event_cb, LV_EVENT_ALL, NULL); 44 | // Set label to Button 45 | auto label = lv_label_create(btnDebug); 46 | lv_label_set_text(label, "Button"); 47 | lv_obj_center(label); 48 | } 49 | 50 | 51 | /** 52 | * Home click 53 | */ 54 | static void btn_event_home(lv_event_t *e){ 55 | // reset everything 56 | navClean(); 57 | // create the new window again 58 | createWindowHome(); 59 | } 60 | 61 | 62 | 63 | static void addEventButtonHome(){ 64 | lv_obj_add_event_cb(homeBtn, btn_event_home, LV_EVENT_CLICKED, NULL); 65 | } 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/apps/blum_app_home.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_APP_HOME_H 2 | #define BLUM_APP_HOME_H 3 | 4 | #include "blum_app_home.cpp" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/apps/blum_app_settings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "core/blum_global.h" 4 | #include "core/blum_status_bar.h" 5 | #include "apps/blum_app_home.h" 6 | #include "hardware/wifi/blum_app_wifi.h" 7 | 8 | static void event_settings_not_implemented(lv_event_t *e){ 9 | lv_obj_t * mbox1 = lv_msgbox_create(NULL, 10 | "Info", "Not yet implemented", NULL, true); 11 | lv_obj_center(mbox1); 12 | } 13 | 14 | /** 15 | * Create the Settings menu for configuring the board 16 | */ 17 | static void createWindowSettings(){ 18 | Serial.println("Create window: Settings"); 19 | // create a usable window to place components 20 | settingsWindow = createWindow("Settings"); 21 | 22 | // content inside the window 23 | lv_obj_t * cont = lv_win_get_content(settingsWindow); 24 | 25 | 26 | lv_obj_t * list = lv_list_create(cont); 27 | // decide where to place the list 28 | lv_obj_set_size(list, lv_pct(100), lv_pct(100)); 29 | lv_obj_center(list); 30 | 31 | 32 | // add the setting items 33 | createListButton(list, LV_SYMBOL_GPS, "LoRa", event_settings_not_implemented); 34 | createListButton(list, LV_SYMBOL_WIFI, "WiFi", event_settings_wifi); 35 | createListButton(list, LV_SYMBOL_BLUETOOTH, "Bluetooth", event_settings_not_implemented); 36 | createListButton(list, LV_SYMBOL_SD_CARD, "Storage", event_settings_not_implemented); 37 | createListButton(list, LV_SYMBOL_BATTERY_FULL, "Power", event_settings_not_implemented); 38 | createListButton(list, LV_SYMBOL_USB, "Expansion ports", event_settings_not_implemented); 39 | createListButton(list, LV_SYMBOL_EYE_OPEN, "Language", event_settings_not_implemented); 40 | } 41 | 42 | 43 | /** 44 | * Settings click 45 | */ 46 | static void btn_event_settings(lv_event_t *e){ 47 | int i = navGetNextFreeIndex(); 48 | // root index, show the settings window 49 | if(i == 1){ 50 | createWindowSettings(); 51 | return; 52 | } 53 | // else this is a "go back" button 54 | navGoBack(); 55 | } 56 | 57 | 58 | static void addEventButtonSettings(){ 59 | lv_obj_add_event_cb(settingBtn, btn_event_settings, LV_EVENT_CLICKED, NULL); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/apps/blum_app_settings.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_APP_SETTINGS_H 2 | #define BLUM_APP_SETTINGS_H 3 | 4 | #include "blum_app_settings.cpp" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/apps/ftp/ftp_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "SdFat.h" 3 | #include "apps/terminal/commands_disk.hpp" 4 | #include 5 | #include 6 | #include "core/blum_global.h" 7 | 8 | #include "ESP32FtpServer.h" 9 | 10 | FtpServer ftpSrv; //set #define FTP_DEBUG in ESP32FtpServer.h to see ftp verbose on serial 11 | 12 | 13 | void setupFTP() 14 | { 15 | Serial.println("Setup FTP"); 16 | // there is no wifi to start 17 | if (wifiEnabled == false) { 18 | Serial.println("FTP not enabled because WiFi isn't available"); 19 | return; 20 | } 21 | 22 | ftpSrv.begin("root","root"); //username, password for ftp. 23 | Serial.println("FTP is running"); 24 | } 25 | 26 | 27 | void loopFTP() { 28 | if (wifiEnabled == false) { 29 | return; 30 | } 31 | ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! 32 | } -------------------------------------------------------------------------------- /src/apps/ftp/ftp_server.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FTP_SERVER_HPP 2 | #define FTP_SERVER_HPP 3 | 4 | extern void setupFTP(); 5 | extern void loopFTP(); 6 | 7 | #endif -------------------------------------------------------------------------------- /src/apps/launcher/app_template.cpp: -------------------------------------------------------------------------------- 1 | // firmware.cpp 2 | 3 | #include 4 | #include 5 | 6 | // Declare the LVGL instance as an extern variable 7 | extern lv_disp_drv_t disp_drv; 8 | 9 | /* 10 | void setup() { 11 | // do not start Serial here, it is already started by the main firmware 12 | } 13 | 14 | void loop() { 15 | Serial.println("Firmware is running"); 16 | // Access the LVGL instance and do something 17 | // do something 18 | } 19 | */ -------------------------------------------------------------------------------- /src/apps/launcher/apps.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * Provides a safe offset for loading the binary from disk storage 6 | */ 7 | uint32_t calculate_flash_offset(File32 file, Stream* response) { 8 | const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); 9 | uint32_t max_binary_app_size = partition->size; 10 | uint32_t file_size = file.size(); 11 | 12 | response->print("Maximum binary app size that can be loaded into partition: "); 13 | response->println(max_binary_app_size); 14 | 15 | response->print("Size of binary app to be loaded: "); 16 | response->println(file_size); 17 | 18 | // Check if file size is larger than available free memory 19 | if (file_size > max_binary_app_size) { 20 | response->println("File size is larger than available free memory"); 21 | return -1; 22 | } 23 | 24 | /* 25 | Ensure that the binary app is placed in a location 26 | that is aligned with the memory requirements of the ESP32, 27 | while also providing enough space for it to run reliably. 28 | */ 29 | uint32_t offset = partition->address + ((max_binary_app_size - file_size) / 2); 30 | return offset; 31 | } 32 | 33 | void writeFromFileToFlash(uint32_t address, File32 file, Stream* response) { 34 | // Open file 35 | if (!file) { 36 | response->println("Failed to open app file"); 37 | return; 38 | } 39 | 40 | // Get file size 41 | size_t fileSize = file.size(); 42 | 43 | // Find partition 44 | const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); 45 | if (!partition) { 46 | response->println("Failed to find partition for writing"); 47 | file.close(); 48 | return; 49 | } 50 | 51 | esp_err_t err; 52 | 53 | 54 | // Erase flash memory 55 | //err = esp_partition_erase_range(partition, address, fileSize); 56 | err = ESP.partitionEraseRange(partition, address, fileSize); 57 | if (err != ESP_OK) { 58 | response->print("Failed to erase flash memory: "); 59 | response->println(esp_err_to_name(err)); 60 | file.close(); 61 | return; 62 | } 63 | 64 | // Allocate buffer for file data 65 | uint32_t* buffer = new uint32_t[fileSize]; 66 | 67 | // Read file data into buffer 68 | if (file.read(buffer, fileSize) != fileSize) { 69 | response->println("Failed to read app file"); 70 | delete[] buffer; 71 | file.close(); 72 | return; 73 | } 74 | 75 | // Write data to flash memory 76 | err = ESP.partitionWrite(partition, address, buffer, fileSize); 77 | //err = esp_partition_write(partition, address, buffer, fileSize); 78 | if (err != ESP_OK) { 79 | response->println("Failed to write data to flash memory"); 80 | delete[] buffer; 81 | file.close(); 82 | return; 83 | } 84 | 85 | // Free buffer memory and close file 86 | delete[] buffer; 87 | file.close(); 88 | 89 | response->println("Data written to flash memory successfully"); 90 | } 91 | 92 | /** 93 | * Runs a binary app. 94 | * Returns false when something went wrong. 95 | */ 96 | boolean runFile(File32 file, Stream* response) { 97 | uint32_t address = calculate_flash_offset(file, response); 98 | if (address == -1) { 99 | response->println("Unable to load app from file"); 100 | return false; 101 | } 102 | response->print("Writing to address: "); 103 | response->println(address); 104 | writeFromFileToFlash(address, file, response); 105 | // now run the app 106 | void (*firmwareEntry)(void) = (void (*)())(address); 107 | firmwareEntry(); 108 | return true; 109 | } 110 | -------------------------------------------------------------------------------- /src/apps/launcher/apps.hpp: -------------------------------------------------------------------------------- 1 | #ifndef apps_hpp 2 | #define apps_hpp 3 | 4 | #include 5 | 6 | //#include "loadFromDisk.cpp" 7 | 8 | extern boolean runFile(File32 file, Stream *response); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/apps/launcher/apps_test1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void writeStringToFile(const char* fileName, const char* data) { 4 | // Open the file for writing in text mode 5 | File file = LittleFS.open(fileName, "w"); 6 | if (!file) { 7 | Serial.println("Failed to open file for writing"); 8 | return; 9 | } 10 | 11 | // Write the string to the file 12 | file.print(data); 13 | 14 | Serial.println("String written to file"); 15 | 16 | Serial.print("Size: "); 17 | Serial.println(file.size()); 18 | 19 | Serial.print("Offset: "); 20 | Serial.println(file.position()); 21 | 22 | 23 | // Close the file 24 | file.close(); 25 | 26 | 27 | } -------------------------------------------------------------------------------- /src/apps/parser/parser.cpp: -------------------------------------------------------------------------------- 1 | #include "apps/terminal/commands_disk.hpp" 2 | #include "wrench.h" 3 | 4 | /** 5 | * Run C-like scripts using Wrench from http://northarc.com/wrench/www/ 6 | * 7 | * Arduino devices have limited memory for uploading programs. 8 | * This means that all programs need to be included inside the 9 | * firmware and uploaded manually by the end-user. 10 | * 11 | * This is nowhere practical for the purpose of B3OS where end-users 12 | * should be permitted to install/uninstall/upgrade individual apps 13 | * without needing to flash a completely new firmware. 14 | * 15 | * So far (in 2023) it is still not possible to run adhoc compiled 16 | * binaries. The best alternative to these compiled binaries are 17 | * script written isn C-like language that is familiar. 18 | * 19 | * C-like scripts that can effectively: 20 | * + create UIs using LVGL 21 | * + integrate with bluetooth, wifi, lora and other peripherals 22 | * + escape the flash-memory restrictions 23 | * + permit people to change/adapt the code 24 | * 25 | * The Wrench C-like interpreter matches all these requirements 26 | * and even more that enable B3OS to launch external apps. 27 | * 28 | * A hearfelt thanks to Curt Hartung for his excellent work. 29 | * 30 | */ 31 | 32 | static Stream* response1; 33 | 34 | const char* wrenchCode = 35 | "print( \"Hello World!\\n\" );" 36 | "for( i=0; i<10; i++ ) " 37 | "{ " 38 | " print( i ); " 39 | "} " 40 | "print(\"\\n\"); "; 41 | 42 | 43 | void print(WRState* w, const WRValue* argv, const int argn, WRValue& retVal, void* usr) { 44 | char buf[1024]; 45 | for (int i = 0; i < argn; ++i) { 46 | response1 -> printf("%s", argv[i].asString(buf, 1024)); 47 | } 48 | } 49 | 50 | void func_parse_c_script(char* args, Stream* response) { 51 | 52 | response1 = response; 53 | 54 | WRState* w = wr_newState(); // create the state 55 | wr_registerFunction(w, "print", print); // bind a function 56 | 57 | unsigned char* outBytes; // compiled code is alloc'ed 58 | int outLen; 59 | 60 | int err = wr_compile(wrenchCode, strlen(wrenchCode), &outBytes, &outLen); // compile it 61 | 62 | if( err == 0) 63 | { 64 | wr_run( w, outBytes, outLen); // load and run the code! 65 | delete[] outBytes; // clean up 66 | } 67 | else 68 | { 69 | response -> print( "Compiling error: "); 70 | response -> println( err); 71 | response -> print( "Did you forget to remove the #define WRENCH_WITHOUT_COMPILER"); 72 | } 73 | 74 | 75 | wr_destroyState(w); 76 | 77 | } -------------------------------------------------------------------------------- /src/apps/parser/parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_HPP 2 | #define PARSER_HPP 3 | 4 | 5 | extern void func_parse_c_script(char *args, Stream *response); 6 | 7 | 8 | #endif -------------------------------------------------------------------------------- /src/apps/terminal/command_parser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is the command parser for the terminal. 3 | It will parse the command string and execute 4 | the corresponding function. 5 | 6 | Files on disk need to end with extension .sh 7 | to be accepted. This parser accepts IF statements 8 | similar to C language. 9 | 10 | Below is an example of possible syntax: 11 | 12 | 13 | -------------------------------------------------------------------- 14 | 15 | // Example script with tags, goto statements, and if statements 16 | 17 | #tag start // define a tag named "start" 18 | 19 | // set up LED pin as output 20 | pinMode(13, OUTPUT); 21 | 22 | // loop through the sequence of blinking the LED 23 | int delay_time = 500; 24 | for (int i = 0; i < 10; i++) { 25 | digitalWrite(13, HIGH); 26 | delay(delay_time); 27 | digitalWrite(13, LOW); 28 | delay(delay_time); 29 | } 30 | 31 | // check if user input is "yes" 32 | if (Serial.available()) { 33 | String input = Serial.readStringUntil('\n'); 34 | if (input == "yes") { 35 | goto confirmation; 36 | } 37 | } 38 | 39 | #tag confirmation // define a tag named "confirmation" 40 | 41 | // print confirmation message 42 | Serial.println("Thank you for your confirmation!"); 43 | 44 | // wait for user input 45 | while (!Serial.available()) { 46 | delay(100); 47 | } 48 | 49 | // check if user input is "done" 50 | String input = Serial.readStringUntil('\n'); 51 | if (input == "done") { 52 | goto end; 53 | } else { 54 | goto confirmation; 55 | } 56 | 57 | #tag end // define a tag named "end" 58 | 59 | // print final message 60 | Serial.println("Script execution complete."); 61 | 62 | -------------------------------------------------------------------- 63 | 64 | */ 65 | 66 | #include // required for Arduino functions and types 67 | #include // required for string functions 68 | #include "SdFat.h" 69 | #include // include the map header file 70 | 71 | #include "commands_disk.hpp" 72 | 73 | bool evalCondition(String condition); 74 | 75 | void func_parse_script(char *args, Stream *response) { 76 | if (args == NULL || args[0] == '\0') { 77 | response->println("No file specified"); 78 | return; 79 | } 80 | 81 | String path = getPath(args); 82 | // open file in read mode 83 | File32 file = sd.open(path, FILE_READ); 84 | if (!file) { 85 | response->print("Failed to open file: " + path); 86 | return; 87 | } 88 | 89 | // define a map to store tag names and line numbers 90 | std::map tags; 91 | // keep track of the current line number 92 | int line_number = 0; 93 | 94 | while (file.available()) { 95 | String line = file.readStringUntil('\n'); 96 | // remove leading/trailing whitespaces 97 | line.trim(); 98 | 99 | // remove comments (starting with '#' or '//') 100 | int comment_pos = line.indexOf('#'); 101 | if (comment_pos != -1) { 102 | line = line.substring(0, comment_pos); 103 | } 104 | comment_pos = line.indexOf("//"); 105 | if (comment_pos != -1) { 106 | line = line.substring(0, comment_pos); 107 | } 108 | 109 | // skip empty lines 110 | if (line.length() == 0) { 111 | continue; 112 | } 113 | 114 | // handle tags (lines starting with '@') 115 | if (line.charAt(0) == '@') { 116 | String tag_name = line.substring(1); 117 | tags[tag_name] = line_number; 118 | continue; 119 | } 120 | 121 | // split the line into command and arguments 122 | int space_pos = line.indexOf(' '); 123 | String command, arguments; 124 | if (space_pos == -1) { 125 | command = line; 126 | } else { 127 | command = line.substring(0, space_pos); 128 | arguments = line.substring(space_pos + 1); 129 | } 130 | 131 | // execute the command 132 | if (command == "delay") { 133 | // usage: delay 134 | long ms = arguments.toInt(); 135 | delay(ms); 136 | } else if (command == "print") { 137 | // usage: print 138 | response->println(arguments); 139 | } else if (command == "pinMode") { 140 | // usage: pinMode 141 | int pin = arguments.substring(0, arguments.indexOf(' ')).toInt(); 142 | int mode = arguments.substring(arguments.indexOf(' ') + 1).toInt(); 143 | pinMode(pin, mode); 144 | } else if (command == "if") { 145 | // usage: if 146 | int space_pos = arguments.indexOf(' '); 147 | String condition = arguments.substring(0, space_pos); 148 | String tag_name = arguments.substring(space_pos + 1); 149 | bool condition_result = evalCondition(condition); 150 | if (condition_result) { 151 | // jump to the specified tag 152 | line_number = tags[tag_name]; 153 | file.seek(line_number); 154 | continue; 155 | } 156 | } else if (command == "goto") { 157 | // usage: goto 158 | String tag_name = arguments; 159 | if (tags.count(tag_name)) { 160 | // jump to the specified tag 161 | line_number = tags[tag_name]; 162 | file.seek(line_number); 163 | continue; 164 | } else { 165 | response->println("Unknown tag: " + tag_name); 166 | } 167 | } else { 168 | response->println("Unknown command: " + command); 169 | } 170 | 171 | // increment the line number 172 | line_number++; 173 | } 174 | file.close(); 175 | } 176 | 177 | bool evalCondition(String condition) { 178 | // supported operators: ==, !=, <, >, <=, >= 179 | int op_pos = condition.indexOf("=="); 180 | if (op_pos != -1) { 181 | String lhs = condition.substring(0, op_pos); 182 | String rhs = condition.substring(op_pos + 2); 183 | return lhs == rhs; 184 | } 185 | op_pos = condition.indexOf("!="); 186 | if (op_pos != -1) { 187 | String lhs = condition.substring(0, op_pos); 188 | String rhs = condition.substring(op_pos + 2); 189 | return lhs != rhs; 190 | } 191 | op_pos = condition.indexOf("<="); 192 | if (op_pos != -1) { 193 | String lhs = condition.substring(0, op_pos); 194 | String rhs = condition.substring(op_pos + 2); 195 | return lhs.toInt() <= rhs.toInt(); 196 | } 197 | op_pos = condition.indexOf(">="); 198 | if (op_pos != -1) { 199 | String lhs = condition.substring(0, op_pos); 200 | String rhs = condition.substring(op_pos + 2); 201 | return lhs.toInt() >= rhs.toInt(); 202 | } 203 | op_pos = condition.indexOf("<"); 204 | if (op_pos != -1) { 205 | String lhs = condition.substring(0, op_pos); 206 | String rhs = condition.substring(op_pos + 1); 207 | return lhs.toInt() < rhs.toInt(); 208 | } 209 | op_pos = condition.indexOf(">"); 210 | if (op_pos != -1) { 211 | String lhs = condition.substring(0, op_pos); 212 | String rhs = condition.substring(op_pos + 1); 213 | return lhs.toInt() > rhs.toInt(); 214 | } 215 | return false; // invalid condition 216 | } -------------------------------------------------------------------------------- /src/apps/terminal/commands_disk.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMANDS_DISK_HPP 2 | #define COMMANDS_DISK_HPP 3 | 4 | #include 5 | #include "Shellminator-IO.hpp" 6 | 7 | #include "SdFat.h" 8 | #include "sdios.h" 9 | 10 | #include "terminal.hpp" 11 | 12 | extern String currentPath; 13 | extern SdFat sd; 14 | 15 | extern void card_initialize(); 16 | extern void func_formatCard(char *args, Stream *response ); 17 | extern String getPath(char *args); 18 | extern void func_touch(char *args, Stream *response); 19 | extern void func_mkdir(char *args, Stream *response ); 20 | extern void func_rm(char *args, Stream *response ); 21 | extern void func_ls(char *args, Stream *response ); 22 | extern void func_ll(char *args, Stream *response ); 23 | extern String getParentFolder(String currentPathEdited); 24 | extern void func_cd(char *args, Stream *response); 25 | extern void func_run(char *args, Stream *response); 26 | extern void func_download(char *args, Stream *response); 27 | extern void func_print(char *args, Stream *response); 28 | 29 | #endif -------------------------------------------------------------------------------- /src/apps/terminal/commands_internal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Shellminator-IO.hpp" 3 | 4 | #include "Commander-API.hpp" 5 | #include "Commander-IO.hpp" 6 | #include "Commander-API-Commands.hpp" 7 | 8 | #include "core/blum_global.h" 9 | #include "terminal.hpp" 10 | 11 | 12 | // prints a line of text 13 | void func_whoami(char *args, Stream *response ){ 14 | response -> print("root"); 15 | } 16 | 17 | 18 | // delays execution for some seconds 19 | void func_wait(char *args, Stream *response ){ 20 | if (args == NULL) { 21 | response->print("No milliseconds specified"); 22 | return; 23 | } 24 | uint32_t value = strtoul(args, NULL, 10); 25 | delay(value); 26 | } 27 | 28 | 29 | // similar to CLEAR function in Linux 30 | void func_clearWiFi(char *args, Stream *response ){ 31 | shellWiFi.clear(); 32 | //Shellminator::clear(response); 33 | } 34 | 35 | // reboot the device 36 | void func_reboot(char *args, Stream *response ){ 37 | 38 | if( strcmp( args, "now" ) == 0 ){ 39 | response -> println( "Please wait, the system will reboot..." ); 40 | return; 41 | } 42 | 43 | response -> print("Reboot the system now?\r\n[y/n]: "); 44 | 45 | if( Shellminator::waitForKey( response, "yY", 30000 ) ){ 46 | response -> println( "Please wait, the system will reboot..." ); 47 | ESP.restart(); 48 | //esp_restart(); 49 | } 50 | 51 | else{ 52 | response -> println( "Reboot aborted!" ); 53 | } 54 | 55 | } 56 | 57 | // prints a line of text 58 | void func_echo(char *args, Stream *response ){ 59 | response -> print(args); 60 | } 61 | 62 | // prints the current version 63 | void func_version(char *args, Stream *response ){ 64 | response -> print(version); 65 | } 66 | 67 | // prints the logo of the operating system 68 | void func_logoWiFi(char *args, Stream *response ){ 69 | //response -> print(logo); 70 | shellWiFi.printBanner(); 71 | } 72 | 73 | /* 74 | // prints the logo of the operating system 75 | void func_exit(char *args, Stream *response ){ 76 | response -> print(0x03); 77 | } 78 | */ 79 | 80 | // sends a beep to the console (not all support this) 81 | void func_beepWiFi(char *args, Stream *response ){ 82 | shellWiFi.beep(); 83 | } 84 | -------------------------------------------------------------------------------- /src/apps/terminal/commands_internal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMANDS_INTERNAL_HPP 2 | #define COMMANDS_INTERNAL_HPP 3 | 4 | #include 5 | #include "Shellminator-IO.hpp" 6 | 7 | #include "terminal.hpp" 8 | 9 | // Function declarations 10 | void func_whoami(char *args, Stream *response ); 11 | void func_wait(char *args, Stream *response ); 12 | void func_clearWiFi(char *args, Stream *response ); 13 | void func_reboot(char *args, Stream *response ); 14 | void func_echo(char *args, Stream *response ); 15 | void func_formatCard(char *args, Stream *response ); 16 | void func_version(char *args, Stream *response ); 17 | void func_logoWiFi(char *args, Stream *response ); 18 | //void func_exit(char *args, Stream *response ); 19 | void func_beepWiFi(char *args, Stream *response ); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/apps/terminal/terminal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TERMINAL_HPP 2 | #define TERMINAL_HPP 3 | 4 | #include 5 | #include "Shellminator-IO.hpp" 6 | 7 | #include 8 | 9 | 10 | extern WiFiServer serverWifi; 11 | extern Shellminator shellWiFi; 12 | extern Shellminator shellSerial; 13 | 14 | // Function declarations 15 | void setupTerminal(); 16 | void loopTerminal(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/apps/terminal/text_editor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAX_BUFFER_SIZE 1024 5 | 6 | const char CMD_HELP[] = "help"; 7 | const char CMD_OPEN[] = "open"; 8 | const char CMD_SAVE[] = "save"; 9 | const char CMD_SAVEAS[] = "saveas"; 10 | const char CMD_INSERT[] = "insert"; 11 | const char CMD_DELETE[] = "delete"; 12 | const char CMD_MOVE[] = "move"; 13 | const char CMD_QUIT[] = "quit"; 14 | 15 | const char MSG_CURSOR[] = "Cursor position: %d\n"; 16 | const char MSG_HELP[] = "Available commands:\n" 17 | "help : Display this message\n" 18 | "open : Open a file for editing\n" 19 | "save : Save changes to the current file\n" 20 | "saveas : Save changes to a new file\n" 21 | "insert : Insert text at the current cursor position\n" 22 | "delete : Delete the next characters\n" 23 | "move : Move the cursor positions to the right\n" 24 | "quit : Quit the editor\n"; 25 | const char MSG_OPEN[] = "Opening file: %s\n"; 26 | const char MSG_OPEN_ERR[] = "Error opening file: %s\n"; 27 | const char MSG_SAVE[] = "Saving changes to file...\n"; 28 | const char MSG_SAVE_ERR[] = "Error saving changes to file: %s\n"; 29 | const char MSG_SAVE_AS[] = "Saving changes to file: %s...\n"; 30 | const char MSG_SAVE_AS_ERR[] = "Error saving changes to file: %s\n"; 31 | const char MSG_INSERT[] = "Enter text to insert: "; 32 | const char MSG_DELETE[] = "Enter count of characters to delete: "; 33 | const char MSG_MOVE[] = "Enter count of positions to move the cursor: "; 34 | const char MSG_QUIT[] = "Are you sure you want to quit? (y/n)\n"; 35 | 36 | char buffer[MAX_BUFFER_SIZE]; 37 | int cursor_pos = 0; -------------------------------------------------------------------------------- /src/blum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "core/blum_global.h" 5 | #include "core/blum_widgets.h" 6 | #include "core/blum_status_bar.h" 7 | #include "core/blum_navigation.h" 8 | #include "apps/blum_app_home.h" 9 | #include "apps/blum_app_settings.h" 10 | #include "hardware/wifi/blum_app_wifi.h" 11 | 12 | /* 13 | 14 | ──▒▒▒▒▒▒───▄████▄ 15 | ─▒─▄▒─▄▒──███▄█▀ 16 | ─▒▒▒▒▒▒▒─▐████──█─█ 17 | ─▒▒▒▒▒▒▒──█████▄ 18 | ─▒─▒─▒─▒───▀████▀ 19 | 20 | ███████████████████████ 21 | █▄─▄─▀█▄▄▄░█─▄▄─█─▄▄▄▄█ 22 | ██─▄─▀██▄▄░█─██─█▄▄▄▄─█ 23 | ▀▄▄▄▄▀▀▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▀ 24 | 25 | The B3 Operating System for Arduino. 26 | 27 | There is an example on the setup() method to help you 28 | getting started with the syntax. You would likely use 29 | this library because: 30 | 31 | + creates standard dialogs (e.g. select wifi network) 32 | + easy to use button/textarea, icons 33 | + keyboard on screen is automatically handled 34 | 35 | 36 | License: Apache-2.0 37 | Copyright (c) radio3.network 38 | URL: https://github.com/radio3-network/B3OS/ 39 | 40 | More projects at https://github.com/radio3-network/ 41 | 42 | 43 | Reference documentation for LVGL: https://docs.lvgl.io/8/ 44 | 45 | 46 | TODO: 47 | + define app content area 48 | ++ respects status bar and keyboard resizing 49 | + clicking on textArea shows keyboard 50 | ++ clicking away from textArea hides keyboard 51 | ++ pressing keyboard button hides/toggles keyboard 52 | + dialog for wifi selection/save/connect 53 | x status bar with icons (e.g. WIFI) 54 | xx include builtin icons 55 | + connectivity icons on status bar 56 | + bluetooth, wifi, lora 57 | + add generic icon functionlity 58 | ++ define PNG source on SD drive 59 | ++ launch an app/script or method 60 | ++ support dark and daylight mode 61 | + dialog for brightness (e.g. manual/automatic) 62 | x code for automatic brightness change 63 | x internal RGB led effects (e.g. breathing) 64 | + switch between screen dark mode and daylight 65 | + app launcher 66 | */ 67 | 68 | 69 | static void loadFlashValues(){ 70 | 71 | Serial.println("Reading the values from flash memory"); 72 | 73 | preferences.begin(NAMESPACE_GENERIC, false); 74 | 75 | // only proceed when there are WIFI keys inside the flash 76 | if(preferences.isKey(KEY_WIFI_ENABLED)){ 77 | // WIFI 78 | wifiEnabled = preferences.getBool(KEY_WIFI_ENABLED, false); 79 | wifi_ssid = preferences.getString(KEY_WIFI_SSID, ""); 80 | wifi_password = preferences.getString(KEY_WIFI_PASSWORD, ""); 81 | }else{ 82 | wifiEnabled = false; 83 | } 84 | 85 | 86 | preferences.end(); 87 | 88 | } 89 | 90 | 91 | static void start(){ 92 | // start the hardware 93 | smartdisplay_init(); 94 | // make the serial line available 95 | Serial.begin(115200); 96 | // wait for serial to be available 97 | while (!Serial); 98 | 99 | // print the logo 100 | Serial.println(""); 101 | Serial.print(logo); 102 | 103 | Serial.println("Starting B3OS"); 104 | // load values in flash memory 105 | loadFlashValues(); 106 | // load wifi 107 | wifiStart(); 108 | // load bluetooth 109 | //setupBluetooth(); 110 | // clear screen and events 111 | lv_obj_clean(lv_scr_act()); 112 | mapWindows = HashMapCreate(); 113 | // add the status bar 114 | buildStatusBar(); 115 | // add the navigation 116 | navClean(); 117 | // add status bar events 118 | addEventButtonHome(); 119 | addEventButtonSettings(); 120 | // initial window 121 | createWindowHome(); 122 | // create the terminal 123 | //setupTerminal(); 124 | setupFTP(); 125 | } 126 | 127 | static void loopBlum(){ 128 | lv_timer_handler(); 129 | loopTerminal(); 130 | loopFTP(); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/core/blum_global.c: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BLUM_GLOBAL_CPP 3 | #define BLUM_GLOBAL_CPP 4 | 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "esp_system.h" 8 | #include "esp_log.h" 9 | #include "driver/uart.h" 10 | 11 | 12 | /* 13 | static void SerialSetup(){ 14 | 15 | 16 | uart_config_t uart_config = { 17 | .baud_rate = 115200, 18 | .data_bits = UART_DATA_8_BITS, 19 | .parity = UART_PARITY_DISABLE, 20 | .stop_bits = UART_STOP_BITS_1, 21 | .flow_ctrl = UART_HW_FLOWCTRL_DISABLE 22 | }; 23 | uart_param_config(UART_NUM_0, &uart_config); 24 | uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); 25 | uart_driver_install(UART_NUM_0, 1024 * 2, 0, 0, NULL, 0); 26 | 27 | 28 | } 29 | 30 | static void SerialWrite(const char *src){ 31 | uart_write_bytes(UART_NUM_0, src, strlen(src)); 32 | } 33 | 34 | static void SerialWriteLn(const char* str) { 35 | size_t len = strlen(str); 36 | char buf[len + 2]; 37 | strcpy(buf, str); 38 | buf[len] = '\n'; 39 | buf[len + 1] = '\0'; 40 | uart_write_bytes(UART_NUM_0, buf, len + 1); 41 | } 42 | 43 | */ 44 | #endif -------------------------------------------------------------------------------- /src/core/blum_global.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_GLOBAL_H 2 | #define BLUM_GLOBAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "hardware/hardware.h" 8 | #include "apps/terminal/terminal.hpp" 9 | #include "apps/launcher/apps.hpp" 10 | #include "apps/ftp/ftp_server.hpp" 11 | 12 | 13 | // global settings 14 | static bool debug = true; 15 | 16 | static String version = "1.0.0"; 17 | 18 | static const char logo[] = 19 | 20 | " \r\n" 21 | " ──▒▒▒▒▒▒───▄████▄ \r\n" 22 | " ─▒─▄▒─▄▒──███▄█▀ \r\n" 23 | " ─▒▒▒▒▒▒▒─▐████──█─█ \r\n" 24 | " ─▒▒▒▒▒▒▒──█████▄ \r\n" 25 | " ─▒─▒─▒─▒───▀████▀ \r\n" 26 | " ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ \r\n" 27 | " █▄─▄─▀█▄▄▄░█─▄▄─█─▄▄▄▄█ \r\n" 28 | " ██─▄─▀██▄▄░█─██─█▄▄▄▄─█ \r\n" 29 | " █▄▄▄▄██▄▄▄▄█▄▄▄▄█▄▄▄▄▄█ \r\n" 30 | " \r\n" 31 | ; 32 | 33 | 34 | 35 | // global objects 36 | static lv_obj_t *kb; 37 | static boolean keyboardvisible = false; 38 | static lv_obj_t *targetTextArea = NULL; 39 | static lv_obj_t *statusBar = NULL; 40 | static lv_obj_t *statusTextLabel; 41 | static lv_obj_t *statusIconWifi; 42 | static lv_obj_t * wifiWindow; 43 | 44 | static lv_obj_t *labelSettingsButton; 45 | static lv_obj_t *settingBtn; 46 | static lv_obj_t *settingsWindow = NULL; 47 | 48 | static lv_obj_t *homeBtn; 49 | static lv_obj_t *homeWindow = NULL; 50 | 51 | static lv_obj_t *previousBtn; 52 | static HashMap* mapWindows = NULL; 53 | 54 | // max number of deep navigation 55 | #define max_navigation_count 10 56 | 57 | // navigation index 58 | static StringArray *indexData; 59 | 60 | 61 | // usable boxes 62 | static lv_obj_t * inputWindow; 63 | 64 | 65 | 66 | // settings saved on flash 67 | 68 | static Preferences preferences; 69 | static const char* NAMESPACE_GENERIC = "internal"; 70 | 71 | // globals for hardware modules 72 | static const char* KEY_WIFI_ENABLED = "WIFI_ENABLED"; 73 | static const char* KEY_WIFI_SSID = "WIFI_SSID"; 74 | static const char* KEY_WIFI_PASSWORD = "WIFI_PASSWORD"; 75 | 76 | static String wifi_ssid = ""; 77 | static String wifi_password = ""; 78 | #define SERVER_PORT 23 79 | 80 | 81 | static boolean wifiEnabled = false; 82 | 83 | 84 | 85 | // hardware-specific definitions 86 | static int screen_width = 320; 87 | static int screen_height = 240; 88 | 89 | 90 | // internal settings 91 | static int statusBarWeight = 30; 92 | 93 | 94 | #include "blum_global.c" 95 | 96 | #endif -------------------------------------------------------------------------------- /src/core/blum_navigation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "blum_global.h" 4 | #include "../utils/utils.h" 5 | 6 | /* 7 | 8 | This file provides support for navigation trees. 9 | 10 | When the user clicks on the menus, this is the 11 | functionality that permits to "go back" between 12 | screens. 13 | 14 | It depends at an HashMap that includes all 15 | active screens, using the screen title as key. 16 | 17 | The HashMap is updated every time a new window 18 | is created at blum_widgets createWindow() 19 | 20 | 21 | // bring the screen forward 22 | lv_obj_move_foreground(settingsWindow); 23 | 24 | 25 | // update the icons 26 | static void statusBarBackButton(){ 27 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_LEFT); 28 | } 29 | 30 | static void statusBarSettingsButton(){ 31 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_SETTINGS); 32 | } 33 | 34 | // update the title 35 | statusBarTextUpdate(title); 36 | 37 | static void statusBarTextUpdate(const char* text){ 38 | lv_label_set_text(statusTextLabel, text ); 39 | } 40 | 41 | 42 | */ 43 | 44 | static int navGetNextFreeIndex() { 45 | return StringArray_size(indexData); 46 | } 47 | 48 | static void navDelete(int indexNumber) { 49 | StringArray_delete(indexData, indexNumber); 50 | } 51 | 52 | /** 53 | * The beginning. Starts with the home window 54 | */ 55 | static void navClean() { 56 | if (indexData != NULL) { 57 | int i = StringArray_size(indexData) - 1; 58 | /*if (i > 0) { 59 | const char *windowPresent = StringArray_get(indexData, i); 60 | lv_obj_t *winPresent = (lv_obj_t *)HashMapGet(mapWindows, windowPresent); 61 | lv_obj_clean(winPresent); 62 | lv_obj_del(winPresent); 63 | }*/ 64 | StringArray_destroy(indexData); 65 | } 66 | // create a new index 67 | indexData = StringArray_create(max_navigation_count); 68 | // reset the icon for settings 69 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_SETTINGS); 70 | } 71 | 72 | static void navNew(const char *title) { 73 | // add it to memory 74 | StringArray_add(indexData, title); 75 | 76 | int i = StringArray_size(indexData) - 1; 77 | const char *key = StringArray_get(indexData, i); 78 | 79 | // change the title on the status bar 80 | lv_label_set_text(statusTextLabel, title); 81 | // add the appropriate icon 82 | if (i == 0) { 83 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_SETTINGS); 84 | } else { 85 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_LEFT); 86 | } 87 | } 88 | 89 | static void navGoBack() { 90 | int i = StringArray_size(indexData) - 2; 91 | const char *windowFuture = StringArray_get(indexData, i); 92 | const char *windowPresent = StringArray_get(indexData, i + 1); 93 | 94 | // using the window name, get the window object 95 | lv_obj_t *winFuture = (lv_obj_t *)HashMapGet(mapWindows, windowFuture); 96 | lv_obj_t *winPresent = (lv_obj_t *)HashMapGet(mapWindows, windowPresent); 97 | 98 | // remove all old items 99 | //lv_obj_move_background(winPresent); 100 | lv_obj_clean(winPresent); 101 | lv_obj_del(winPresent); 102 | 103 | // bring this window object to the foreground 104 | lv_obj_move_foreground(winFuture); 105 | 106 | lv_task_handler(); 107 | // update the label 108 | lv_label_set_text(statusTextLabel, windowFuture); 109 | 110 | // means we are on the root again 111 | if (i == 0) { 112 | // delete the memory map 113 | HashMapClear(mapWindows); 114 | // add again the root window 115 | HashMapInsert(mapWindows, windowFuture, winFuture); 116 | // restore the icon and index 117 | navClean(); 118 | // add home again on the index 119 | navNew(windowFuture); 120 | // stop it here 121 | return; 122 | } 123 | 124 | // delete the current index memory 125 | navDelete(i + 1); 126 | } 127 | -------------------------------------------------------------------------------- /src/core/blum_navigation.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_NAVIGATION_H 2 | #define BLUM_NAVIGATION_H 3 | 4 | #include "blum_navigation.cpp" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/core/blum_status_bar.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "blum_global.h" 5 | 6 | 7 | 8 | static void statusBarBackButton(){ 9 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_LEFT); 10 | } 11 | 12 | static void statusBarSettingsButton(){ 13 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_SETTINGS); 14 | } 15 | 16 | /** 17 | * Update the status bar text 18 | */ 19 | static void statusBarTextUpdate(const char* text){ 20 | lv_label_set_text(statusTextLabel, text ); 21 | } 22 | 23 | 24 | 25 | static void iconWifiCreate(){ 26 | if(statusIconWifi != NULL){ 27 | return; 28 | } 29 | statusIconWifi = lv_label_create(statusBar); 30 | lv_label_set_text(statusIconWifi, LV_SYMBOL_WIFI); 31 | lv_obj_align(statusIconWifi, LV_ALIGN_RIGHT_MID, -20, 0); 32 | Serial.println("Wifi: Turn ON"); 33 | //lv_obj_invalidate(statusBar); 34 | } 35 | 36 | static void iconWifiRemove(){ 37 | if(statusIconWifi == NULL){ 38 | return; 39 | } 40 | lv_obj_clean(statusIconWifi); 41 | lv_label_set_text(statusIconWifi, LV_SYMBOL_DUMMY); 42 | lv_obj_del(statusIconWifi); 43 | statusIconWifi = NULL; 44 | Serial.println("Wifi: Turn OFF"); 45 | //lv_obj_invalidate(statusBar); 46 | } 47 | 48 | /** 49 | * Create the status bar always present on top of the device 50 | */ 51 | static void buildStatusBar() { 52 | 53 | Serial.println("Build status bar"); 54 | 55 | 56 | static lv_style_t style_btn; 57 | lv_style_init(&style_btn); 58 | lv_style_set_bg_color(&style_btn, lv_color_hex(0xC5C5C5)); 59 | lv_style_set_bg_opa(&style_btn, LV_OPA_50); 60 | 61 | statusBar = lv_obj_create(lv_scr_act()); 62 | lv_obj_set_size(statusBar, screen_width, 30); 63 | lv_obj_align(statusBar, LV_ALIGN_TOP_MID, 0, -1); 64 | lv_obj_remove_style(statusBar, NULL, LV_PART_SCROLLBAR | LV_STATE_ANY); 65 | 66 | // make sure the status bar does not move (scroll) elsewhere 67 | lv_obj_set_scroll_dir(statusBar, LV_DIR_NONE); 68 | 69 | // status label 70 | statusTextLabel = lv_label_create(statusBar); 71 | lv_obj_set_size(statusTextLabel, screen_width - 50, 30); 72 | lv_label_set_text(statusTextLabel, " " ); 73 | lv_obj_align(statusTextLabel, LV_ALIGN_LEFT_MID, 28, 7); 74 | 75 | // wifi icon 76 | if(isWiFiConnected()){ 77 | iconWifiCreate(); 78 | } 79 | 80 | // home button 81 | homeBtn = lv_btn_create(statusBar); 82 | lv_obj_set_size(homeBtn, 30, 30); 83 | lv_obj_align(homeBtn, LV_ALIGN_LEFT_MID, -15, 0); 84 | lv_obj_t *labelHome = lv_label_create(homeBtn); 85 | lv_label_set_text(labelHome, LV_SYMBOL_HOME); 86 | lv_obj_center(labelHome); 87 | 88 | // settings button 89 | settingBtn = lv_btn_create(statusBar); 90 | lv_obj_set_size(settingBtn, 30, 30); 91 | lv_obj_align(settingBtn, LV_ALIGN_RIGHT_MID, 15, 0); 92 | labelSettingsButton = lv_label_create(settingBtn); 93 | lv_label_set_text(labelSettingsButton, LV_SYMBOL_SETTINGS); 94 | lv_obj_center(labelSettingsButton); 95 | } 96 | -------------------------------------------------------------------------------- /src/core/blum_status_bar.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_STATUS_BAR_H 2 | #define BLUM_STATUS_BAR_H 3 | 4 | #include "blum_status_bar.cpp" 5 | 6 | #endif 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/core/blum_widgets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utils/utils.h" 4 | #include "blum_global.h" 5 | #include "blum_status_bar.h" 6 | #include "core/blum_navigation.h" 7 | 8 | 9 | static lv_obj_t * createListButton( 10 | lv_obj_t * list, const void *icon, 11 | const char *txt, lv_event_cb_t event_cb){ 12 | lv_obj_t * btn = lv_list_add_btn(list, icon, txt); 13 | lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL); 14 | return btn; 15 | } 16 | 17 | /** 18 | * Create an empty window to clear up the screen 19 | */ 20 | static void createWindowEmpty(){ 21 | lv_obj_t* win = lv_win_create(lv_scr_act(), 0); 22 | lv_obj_set_size(win, LV_HOR_RES, LV_VER_RES - statusBarWeight); 23 | lv_obj_align(win, LV_ALIGN_BOTTOM_MID, 0, 0); 24 | } 25 | 26 | 27 | 28 | static lv_obj_t* createWindow(const char *title){ 29 | 30 | lv_obj_t* win; 31 | createWindowEmpty(); 32 | 33 | // avoid creating a new window when it already exists 34 | if(HashMapContainsKey(mapWindows, title)){ 35 | // provide the previously created window 36 | lv_obj_t* existingWindow = (lv_obj_t*) HashMapGet(mapWindows, title); 37 | return existingWindow; 38 | }else{ 39 | // define a window with a 0 sized header (to hide it) 40 | win = lv_win_create(lv_scr_act(), 0); 41 | // add to the map 42 | HashMapInsert(mapWindows, title, (void*)win); 43 | // use all screen except the top because of the status bar 44 | lv_obj_set_size(win, LV_HOR_RES, LV_VER_RES - statusBarWeight); 45 | lv_obj_align(win, LV_ALIGN_BOTTOM_MID, 0, 0); 46 | lv_win_add_title(win, title); 47 | } 48 | 49 | // add to the index 50 | navNew(title); 51 | 52 | return win; 53 | } 54 | 55 | /* 56 | Create a button at specific coordinates 57 | */ 58 | static lv_obj_t* createButton( 59 | lv_coord_t x, lv_coord_t y, 60 | lv_coord_t length, lv_coord_t height, 61 | const char* text, lv_event_cb_t event_toLaunch){ 62 | // Create a buttom 63 | lv_obj_t *btn = lv_btn_create(lv_scr_act()); 64 | lv_obj_set_pos(btn, x, y); 65 | lv_obj_set_size(btn, length, height); 66 | lv_obj_add_event_cb(btn, event_toLaunch, LV_EVENT_ALL, NULL); 67 | // Set label to Button 68 | lv_obj_t *label = lv_label_create(btn); 69 | lv_label_set_text(label, text); 70 | lv_obj_center(label); 71 | return btn; 72 | } 73 | 74 | 75 | /** 76 | * Create a roller with multiple choices 77 | */ 78 | static lv_obj_t* createRoller( 79 | lv_coord_t x, lv_coord_t y, 80 | lv_coord_t length, lv_coord_t height, 81 | const char *items, lv_event_cb_t event_toLaunch 82 | ){ 83 | lv_obj_t *scr = lv_scr_act(); 84 | lv_obj_t* roller = lv_roller_create(scr); 85 | //lv_obj_set_layout(roller, LV_LAYOUT_FLEX); 86 | lv_obj_set_pos(roller, x, y); 87 | lv_obj_set_size(roller, length, height); 88 | //lv_roller_set_visible_row_count(roller, 4); 89 | lv_roller_set_options(roller, 90 | items, 91 | LV_ROLLER_MODE_NORMAL); 92 | //lv_roller_set_selected(roller, 0, LV_ANIM_ON); 93 | // associate an event when clicking the items 94 | if(event_toLaunch != NULL){ 95 | lv_obj_add_event_cb(roller, event_toLaunch, LV_EVENT_ALL, NULL); 96 | } 97 | return roller; 98 | } 99 | 100 | 101 | 102 | /** 103 | * Specific click on the settings button to 104 | * display the settings dialog 105 | */ 106 | static void btn_event_keyboardKeypress(lv_event_t *e) 107 | { 108 | lv_event_code_t code = lv_event_get_code(e); 109 | lv_obj_t *btn = lv_event_get_target(e); 110 | 111 | if (code == LV_EVENT_VALUE_CHANGED) { 112 | //lv_kb_def_event_cb(kb, event); // call the default event handler 113 | //printf("Key pressed");//: %c\n", (char)(*key)); // print the key value as a character 114 | char time_buffer[14]; 115 | sprintf(time_buffer, "%d", millis()); 116 | statusBarTextUpdate(time_buffer); 117 | return; 118 | } 119 | 120 | if (code == LV_EVENT_CANCEL) { 121 | statusBarTextUpdate("Cancel"); 122 | return; 123 | } 124 | 125 | 126 | 127 | if (code == LV_EVENT_CLICKED) 128 | { 129 | printf("Key pressed2"); 130 | // log("Key pressed", 1); 131 | // log("code", code); 132 | // log("button", btn); 133 | 134 | } 135 | } 136 | 137 | /** 138 | * Shows a keyboard on the screen. 139 | * This keyboard needs to be attached to 140 | * a target object where the typed text 141 | * will be visible. 142 | */ 143 | static void keyboardShow(lv_obj_t *targetTextArea){ 144 | kb = lv_keyboard_create(lv_scr_act()); 145 | // use half of the screen available (expect 320x240 resolution) 146 | lv_obj_set_size(kb, LV_HOR_RES-2, LV_VER_RES / 2); 147 | //LV_ALIGN_CENTER 148 | //TODO: this align default might break keyboard position 149 | lv_obj_align(kb, LV_ALIGN_DEFAULT, LV_DIR_RIGHT, LV_VER_RES / 2); 150 | 151 | // remove the event in case it had been added previously 152 | lv_obj_remove_event_cb(kb, btn_event_keyboardKeypress); 153 | lv_obj_add_event_cb(kb, btn_event_keyboardKeypress, LV_EVENT_ALL, NULL); 154 | lv_keyboard_set_textarea(kb, targetTextArea); 155 | keyboardvisible = true; 156 | 157 | // raise the focus 158 | lv_group_t *group = lv_group_create(); // create a group 159 | lv_group_add_obj(group, targetTextArea); // add the object to the group 160 | lv_group_add_obj(group, kb); // add the object to the group 161 | lv_group_focus_obj(targetTextArea); // set focus on the object 162 | 163 | } 164 | 165 | static void keyboardHide(){ 166 | if(keyboardvisible == false){ 167 | return; 168 | } 169 | // hide the keyboard 170 | lv_obj_del(kb); 171 | keyboardvisible = false; 172 | } 173 | 174 | 175 | static void btn_event_togglekeyboard(lv_event_t *e){ 176 | lv_event_code_t code = lv_event_get_code(e); 177 | lv_obj_t *btn = lv_event_get_target(e); 178 | if (code == LV_EVENT_CLICKED) 179 | { 180 | if(keyboardvisible){ 181 | keyboardHide(); 182 | }else{ 183 | keyboardShow(targetTextArea); 184 | } 185 | } 186 | } 187 | 188 | /** 189 | * When clicking inside a text area, show the keyboard 190 | */ 191 | static void btn_event_textAreaClickedShowKeyboard(lv_event_t *e) 192 | { 193 | lv_event_code_t code = lv_event_get_code(e); 194 | lv_obj_t *btn = lv_event_get_target(e); 195 | if (code == LV_EVENT_CLICKED) 196 | { 197 | if(keyboardvisible == false){ 198 | keyboardShow(targetTextArea); 199 | } 200 | } 201 | } 202 | 203 | 204 | /** 205 | * Creates a text area at the given coordinates 206 | */ 207 | static lv_obj_t* createTextArea(lv_coord_t x, lv_coord_t y, 208 | lv_coord_t width, lv_coord_t height, const char *text){ 209 | lv_obj_t *scr = lv_scr_act(); 210 | lv_obj_t *textArea = lv_textarea_create(scr); 211 | lv_obj_set_x(textArea, x); 212 | lv_obj_set_y(textArea, y); 213 | lv_obj_set_width(textArea, width); 214 | lv_obj_set_height(textArea, height); 215 | lv_textarea_set_placeholder_text(textArea, text); 216 | targetTextArea = textArea; 217 | //TODO when user clicks on text area, the keyboard must show up 218 | lv_obj_add_event_cb(textArea, btn_event_textAreaClickedShowKeyboard, LV_EVENT_ALL, NULL); 219 | //TODO when the user clicks outside the text area, the keyboard goes away 220 | //TODO define this as an event for the text area 221 | return textArea; 222 | } 223 | 224 | -------------------------------------------------------------------------------- /src/core/blum_widgets.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_WIDGETS_H 2 | #define BLUM_WIDGETS_H 3 | 4 | #include "blum_widgets.cpp" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/core/snippets.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Simple snippets for every day programming 4 | 5 | 6 | char titleId[50]; 7 | sprintf(titleId, "%s %d", key, i); 8 | lv_label_set_text(statusTextLabel, titleId); 9 | 10 | const char * key = StringArray_get(indexData, i); 11 | 12 | lv_label_set_text(statusTextLabel, key); 13 | 14 | 15 | 16 | // convert a char to string and compare the first character 17 | char* args = "/test"; // example command line argument 18 | String argString = String(args); // convert char* to String 19 | if (argString.startsWith("/")) { // check if string starts with "/" 20 | Serial.println("The argument starts with '/'"); 21 | } else { 22 | Serial.println("The argument does not start with '/'"); 23 | } 24 | 25 | 26 | 27 | // get the state of the switch from EEPROM memory 28 | if (wifi_enabled != nullptr) { 29 | if(strcmp(wifi_enabled, "ON") == 0){ 30 | lv_obj_add_state(sw, LV_STATE_CHECKED); 31 | }else{ 32 | lv_obj_add_state(sw, LV_STATE_DEFAULT); 33 | } 34 | } 35 | 36 | // add an event 37 | lv_obj_add_event_cb(homeBtn, btn_event_home, LV_EVENT_CLICKED, NULL); 38 | 39 | static void btn_event_home(lv_event_t *e){ 40 | 41 | } 42 | 43 | 44 | 45 | */ -------------------------------------------------------------------------------- /src/hardware/bluetooth/TerminalBluetooth.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /* 5 | #ifndef TERMINAL_BLUE 6 | #define TERMINAL_BLUE 7 | 8 | 9 | #include "BluetoothSerial.h" 10 | 11 | 12 | static BluetoothSerial SerialBT2; 13 | static String BluetoothSerialMessage = ""; 14 | 15 | /* 16 | // Check if Bluetooth configs are enabled 17 | #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) 18 | #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it 19 | #endif 20 | 21 | static void setupTerminalBluetooth(){ 22 | 23 | SerialBT2.begin("ESP32"); 24 | 25 | } 26 | 27 | #endif 28 | */ -------------------------------------------------------------------------------- /src/hardware/boards/ESP2866_NodeMCU_V3.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | NodeMCU V3 is basically an ESP2866 standalone board without screen. 7 | 8 | Keep in attention: 9 | 10 | + Change platformio.ini file to include the following: 11 | 12 | [env:nodemcu3] 13 | platform = espressif8266 14 | board = nodemcu3 15 | framework = arduino 16 | 17 | + Adjust the settings for your external screen. 18 | On this config board we used a 2.8 TFT SPI 240x320 V1.2 display. 19 | 20 | The display is an ILI9341 LCD model with XPT2046 touch screen. 21 | Purchase link: https://www.aliexpress.com/item/32795636902.html 22 | 23 | 24 | TFT Screen CS (Chip Select) pin to NodeMCU D2 (GPIO4) 25 | TFT Screen DC (Data/Command) pin to NodeMCU D1 (GPIO5) 26 | TFT Screen MOSI (Master Out Slave In) pin to NodeMCU D7 (GPIO13) 27 | TFT Screen SCK (Serial Clock) pin to NodeMCU D5 (GPIO14) 28 | TFT Screen MISO (Master In Slave Out) pin to NodeMCU D6 (GPIO12) 29 | TFT Screen RESET pin to NodeMCU D0 (GPIO16) 30 | 31 | Touch Screen CS (Chip Select) pin to NodeMCU D3 (GPIO0) 32 | Touch Screen IRQ (Interrupt) pin to NodeMCU D4 (GPIO2) 33 | Touch Screen MOSI (Master Out Slave In) pin to NodeMCU D7 (GPIO13) 34 | Touch Screen SCK (Serial Clock) pin to NodeMCU D5 (GPIO14) 35 | Touch Screen MISO (Master In Slave Out) pin to NodeMCU D6 (GPIO12) 36 | Touch Screen GND to NodeMCU GND 37 | Touch Screen VCC to NodeMCU 3.3V (or an external power supply) 38 | 39 | 40 | */ 41 | 42 | 43 | #define TFT_WIDTH 240 44 | #define TFT_HEIGHT 320 45 | 46 | #define TFT_PANEL_ORDER_BGR 47 | 48 | #define HAS_BOARD 49 | #define HAS_DISPLAY 50 | #define HAS_TOUCH 51 | 52 | // screen model and pins 53 | #define ILI9431 54 | #define ILI9431_SPI_SCLK 14 55 | #define ILI9431_SPI_MOSI 13 56 | #define ILI9431_SPI_MISO 12 57 | #define ILI9341_PIN_CS 04 58 | #define ILI9341_PIN_DC 05 59 | #define ILI9341_SPI_FREQ 80000000 60 | #define ILI9341_PIN_BL 21 61 | #define ILI9341_PWM_CHANNEL_BL 12 62 | 63 | #define ILI9341_PWM_FREQ_BL 5000 64 | #define ILI9341_PWM_BITS_BL 8 65 | #define ILI9341_PWM_MAX_BL ((1 << ILI9341_PWM_BITS_BL) - 1) 66 | 67 | // touch screen model and pins 68 | #define XPT2046 69 | #define XPT2046_SPI_SCLK 25 70 | #define XPT2046_SPI_MOSI 32 71 | #define XPT2046_SPI_MISO 39 72 | #define XPT2046_SPI_FREQ 2000000 73 | #define XPT2046_PIN_INT 36 74 | #define XPT2046_PIN_CS 33 75 | // Calibration 240x320 76 | #define XPT2046_MIN_X 349 77 | #define XPT2046_MAX_X 3859 78 | #define XPT2046_MIN_Y 247 79 | #define XPT2046_MAX_Y 3871 80 | 81 | // Build in RGB LED 82 | #define LED_PIN_R 18 83 | #define LED_PIN_G 16 84 | #define LED_PIN_B 17 85 | // PWM channels for RGB 86 | #define LED_PWM_FREQ 5000 87 | #define LED_PWM_CHANNEL_R 13 88 | #define LED_PWM_CHANNEL_G 14 89 | #define LED_PWM_CHANNEL_B 15 90 | #define LED_PWM_BITS 8 91 | #define LED_PWM_MAX ((1 << LED_PWM_BITS) - 1) 92 | 93 | // Photo resistor 94 | #define CDS_PIN 34 // ANALOG_PIN_0 95 | 96 | // Audio out 97 | #define AUDIO_PIN 26 98 | 99 | // TF Card 100 | 101 | #define TF_CARD 102 | #define TF_PIN_CS 5 103 | #define TS_PIN_MOSI 23 104 | #define TF_PIN_SCLK 18 105 | #define TF_PIN_MISC 19 106 | 107 | 108 | extern SPIClass spi_ili9431; 109 | extern SPIClass spi_xpt2046; 110 | 111 | static void setupHardwareInterfaces(){ 112 | spi_ili9431.begin(ILI9431_SPI_SCLK, ILI9431_SPI_MISO, ILI9431_SPI_MOSI); 113 | spi_xpt2046.begin(XPT2046_SPI_SCLK, XPT2046_SPI_MISO, XPT2046_SPI_MOSI); 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/hardware/boards/ESP32_2432S028R_BGR.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ESP32_2432S028R 6 | #define TFT_WIDTH 240 7 | #define TFT_HEIGHT 320 8 | 9 | #define TFT_PANEL_ORDER_BGR 10 | 11 | #define HAS_BOARD 12 | #define HAS_DISPLAY 13 | #define HAS_TOUCH 14 | 15 | // screen model and pins 16 | #define ILI9431 17 | #define ILI9431_SPI_SCLK 14 18 | #define ILI9431_SPI_MOSI 13 19 | #define ILI9431_SPI_MISO 12 20 | #define ILI9341_PIN_CS 15 21 | #define ILI9341_PIN_DC 2 22 | #define ILI9341_SPI_FREQ 40000000 23 | #define ILI9341_PIN_BL 21 24 | #define ILI9341_PWM_CHANNEL_BL 7 25 | #define ILI9341_PWM_FREQ_BL 44100 26 | #define ILI9341_PWM_BITS_BL 8 27 | #define ILI9341_PWM_MAX_BL ((1 << ILI9341_PWM_BITS_BL) - 1) 28 | 29 | // touch screen model and pins 30 | #define XPT2046 31 | #define XPT2046_SPI_SCLK 25 32 | #define XPT2046_SPI_MOSI 32 33 | #define XPT2046_SPI_MISO 39 34 | #define XPT2046_SPI_FREQ 1000000 35 | //#define XPT2046_SPI_FREQ 2000000 36 | #define XPT2046_PIN_INT 36 37 | #define XPT2046_PIN_CS 33 38 | // Calibration 240x320 39 | #define XPT2046_MIN_X 300 40 | #define XPT2046_MAX_X 3900 41 | #define XPT2046_MIN_Y 200 42 | #define XPT2046_MAX_Y 3700 43 | 44 | 45 | // Build in RGB LED 46 | #define LED_PIN_R 4 47 | #define LED_PIN_G 16 48 | #define LED_PIN_B 17 49 | // PWM channels for RGB 50 | #define LED_PWM_FREQ 5000 51 | #define LED_PWM_CHANNEL_R 13 52 | #define LED_PWM_CHANNEL_G 14 53 | #define LED_PWM_CHANNEL_B 15 54 | #define LED_PWM_BITS 8 55 | #define LED_PWM_MAX ((1 << LED_PWM_BITS) - 1) 56 | 57 | // Photo resistor 58 | #define CDS_PIN 34 // ANALOG_PIN_0 59 | 60 | // Audio out 61 | #define AUDIO_PIN 26 62 | 63 | // TF Card 64 | #define TF_PIN_CS 5 65 | #define TS_PIN_MOSI 23 66 | #define TF_PIN_SCLK 18 67 | #define TF_PIN_MISC 19 68 | 69 | 70 | extern SPIClass spi_ili9431; 71 | extern SPIClass spi_xpt2046; 72 | 73 | static void setupHardwareInterfaces(){ 74 | spi_ili9431.begin(ILI9431_SPI_SCLK, ILI9431_SPI_MISO, ILI9431_SPI_MOSI); 75 | spi_xpt2046.begin(XPT2046_SPI_SCLK, XPT2046_SPI_MISO, XPT2046_SPI_MOSI); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/hardware/boards/ESP32_2432S028R_RGB.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ESP32_2432S028R 6 | #define TFT_WIDTH 240 7 | #define TFT_HEIGHT 320 8 | 9 | #define TFT_PANEL_ORDER_RGB 10 | 11 | #define HAS_BOARD 12 | #define HAS_DISPLAY 13 | #define HAS_TOUCH 14 | 15 | // screen model and pins 16 | #define ILI9431 17 | #define ILI9431_SPI_SCLK 14 18 | #define ILI9431_SPI_MOSI 13 19 | #define ILI9431_SPI_MISO 12 20 | #define ILI9341_PIN_CS 15 21 | #define ILI9341_PIN_DC 2 22 | #define ILI9341_SPI_FREQ 80000000 23 | #define ILI9341_PIN_BL 21 24 | #define ILI9341_PWM_CHANNEL_BL 12 25 | #define ILI9341_PWM_FREQ_BL 5000 26 | #define ILI9341_PWM_BITS_BL 8 27 | #define ILI9341_PWM_MAX_BL ((1 << ILI9341_PWM_BITS_BL) - 1) 28 | 29 | // touch screen model and pins 30 | #define XPT2046 31 | #define XPT2046_SPI_SCLK 25 32 | #define XPT2046_SPI_MOSI 32 33 | #define XPT2046_SPI_MISO 39 34 | #define XPT2046_SPI_FREQ 2000000 35 | #define XPT2046_PIN_INT 36 36 | #define XPT2046_PIN_CS 33 37 | // Calibration 240x320 38 | #define XPT2046_MIN_X 349 39 | #define XPT2046_MAX_X 3859 40 | #define XPT2046_MIN_Y 247 41 | #define XPT2046_MAX_Y 3871 42 | 43 | // Build in RGB LED 44 | #define LED_PIN_R 4 45 | #define LED_PIN_G 16 46 | #define LED_PIN_B 17 47 | // PWM channels for RGB 48 | #define LED_PWM_FREQ 5000 49 | #define LED_PWM_CHANNEL_R 13 50 | #define LED_PWM_CHANNEL_G 14 51 | #define LED_PWM_CHANNEL_B 15 52 | #define LED_PWM_BITS 8 53 | #define LED_PWM_MAX ((1 << LED_PWM_BITS) - 1) 54 | 55 | // Photo resistor 56 | #define CDS_PIN 34 // ANALOG_PIN_0 57 | 58 | // Audio out 59 | #define AUDIO_PIN 26 60 | 61 | // TF Card 62 | #define TF_PIN_CS 5 63 | #define TS_PIN_MOSI 23 64 | #define TF_PIN_SCLK 18 65 | #define TF_PIN_MISC 19 66 | 67 | 68 | extern SPIClass spi_ili9431; 69 | extern SPIClass spi_xpt2046; 70 | 71 | static void setupHardwareInterfaces(){ 72 | spi_ili9431.begin(ILI9431_SPI_SCLK, ILI9431_SPI_MISO, ILI9431_SPI_MOSI); 73 | spi_xpt2046.begin(XPT2046_SPI_SCLK, XPT2046_SPI_MISO, XPT2046_SPI_MOSI); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/hardware/boards/ESP32_3248S035C.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #define TFT_WIDTH 320 8 | #define TFT_HEIGHT 480 9 | #define ST7796 10 | #define ST7796_SPI_SCLK 14 11 | #define ST7796_SPI_MOSI 13 12 | #define ST7796_SPI_MISO 12 13 | #define ST7796_PIN_CS 15 14 | #define ST7796_PIN_DC 2 15 | #define ST7796_SPI_FREQ 80000000 16 | #define ST7796_PIN_BL 27 17 | #define ST7796_PWM_CHANNEL_BL 12 18 | #define ST7796_PWM_FREQ_BL 5000 19 | #define ST7796_PWM_BITS_BL 8 20 | #define ST7796_PWM_MAX_BL ((1 << ST7796_PWM_BITS_BL) - 1) 21 | #define GT911 22 | #define GT911_IIC_SDA 33 23 | #define GT911_IIC_SCL 32 24 | #define GT911_IIC_RST 25 25 | 26 | // Build in RGB LED 27 | #define LED_PIN_R 4 28 | #define LED_PIN_G 16 29 | #define LED_PIN_B 17 30 | // PWM channels for RGB 31 | #define LED_PWM_FREQ 5000 32 | #define LED_PWM_CHANNEL_R 13 33 | #define LED_PWM_CHANNEL_G 14 34 | #define LED_PWM_CHANNEL_B 15 35 | #define LED_PWM_BITS 8 36 | #define LED_PWM_MAX ((1 << LED_PWM_BITS) - 1) 37 | 38 | // Photo resistor 39 | #define CDS_PIN 34 // ANALOG_PIN_0 40 | 41 | // Audio out 42 | #define AUDIO_PIN 26 43 | 44 | // TF Card 45 | #define TF_PIN_CS 5 46 | #define TS_PIN_MOSI 23 47 | #define TF_PIN_SCLK 18 48 | #define TF_PIN_MISC 19 49 | 50 | 51 | static SPIClass spi_st7796; 52 | static TwoWire i2c_gt911 = TwoWire(1); // Bus number 1 53 | 54 | 55 | static void setupHardwareInterfaces(){ 56 | spi_st7796.begin(ST7796_SPI_SCLK, ST7796_SPI_MISO, ST7796_SPI_MOSI); 57 | i2c_gt911.begin(GT911_IIC_SDA, GT911_IIC_SCL); 58 | } -------------------------------------------------------------------------------- /src/hardware/boards/ESP32_3248S035R.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ESP32_3248S035R 6 | #define TFT_WIDTH 320 7 | #define TFT_HEIGHT 480 8 | #define ST7796 9 | #define ST7796_SPI_SCLK 14 10 | #define ST7796_SPI_MOSI 13 11 | #define ST7796_SPI_MISO 12 12 | #define ST7796_PIN_CS 15 13 | #define ST7796_PIN_DC 2 14 | #define ST7796_SPI_FREQ 80000000 15 | #define ST7796_PIN_BL 27 16 | #define ST7796_PWM_CHANNEL_BL 12 17 | #define ST7796_PWM_FREQ_BL 5000 18 | #define ST7796_PWM_BITS_BL 8 19 | #define ST7796_PWM_MAX_BL ((1 << ST7796_PWM_BITS_BL) - 1) 20 | #define XPT2046 21 | #define XPT2046_SPI_SCLK 14 22 | #define XPT2046_SPI_MOSI 13 23 | #define XPT2046_SPI_MISO 12 24 | #define XPT2046_SPI_FREQ 2000000 25 | #define XPT2046_PIN_INT 36 26 | #define XPT2046_PIN_CS 33 27 | 28 | // Calibration 320x480 29 | #define XPT2046_MIN_X 256 30 | #define XPT2046_MAX_X 3860 31 | #define XPT2046_MIN_Y 180 32 | #define XPT2046_MAX_Y 3900 33 | 34 | 35 | // Build in RGB LED 36 | #define LED_PIN_R 4 37 | #define LED_PIN_G 16 38 | #define LED_PIN_B 17 39 | // PWM channels for RGB 40 | #define LED_PWM_FREQ 5000 41 | #define LED_PWM_CHANNEL_R 13 42 | #define LED_PWM_CHANNEL_G 14 43 | #define LED_PWM_CHANNEL_B 15 44 | #define LED_PWM_BITS 8 45 | #define LED_PWM_MAX ((1 << LED_PWM_BITS) - 1) 46 | 47 | // Photo resistor 48 | #define CDS_PIN 34 // ANALOG_PIN_0 49 | 50 | // Audio out 51 | #define AUDIO_PIN 26 52 | 53 | // TF Card 54 | #define TF_PIN_CS 5 55 | #define TS_PIN_MOSI 23 56 | #define TF_PIN_SCLK 18 57 | #define TF_PIN_MISC 19 58 | 59 | 60 | static SPIClass spi_st7796; 61 | #define spi_xpt2046 spi_st7796 62 | 63 | static void setupHardwareInterfaces(){ 64 | spi_st7796.begin(ST7796_SPI_SCLK, ST7796_SPI_MISO, ST7796_SPI_MOSI); 65 | // xpy2046 uses same SPI bus 66 | } -------------------------------------------------------------------------------- /src/hardware/choose.h: -------------------------------------------------------------------------------- 1 | #ifndef CHOOSE_H 2 | #define CHOOSE_H 3 | 4 | /* 5 | 6 | This is the place to choose the type of board 7 | that you want to support. Look on the bottom 8 | of this file and uncomment to choose the board 9 | that you are using. 10 | 11 | */ 12 | 13 | 14 | //// Boards with CPU + Display + Touch //// 15 | 16 | 17 | // Sunton boards 18 | // purchase: https://www.aliexpress.com/item/1005004502250619.html 19 | 20 | #include "boards/ESP32_2432S028R_BGR.h" 21 | //#include "boards/ESP32_2432S028R_RGB.h" 22 | //#include "boards/ESP32_3248S035C.h" 23 | //#include "boards/ESP32_3248S035R.h" 24 | 25 | 26 | // ESP8266 boards 27 | //#include "boards/ESP2866_NodeMCU_V3.h" 28 | 29 | 30 | 31 | // verify that a minimal hardware has been set 32 | #if !defined(HAS_BOARD) && !defined(HAS_DISPLAY) && !defined(HAS_TOUCH) 33 | #error To compile this code, please open hardware/choose.h and choose your hardware 34 | #endif 35 | 36 | 37 | /* 38 | 39 | Board not listed here yet? 40 | 41 | In case you don't see your board here, copy 42 | one of the existing files and use as template. 43 | 44 | You can also ask for help on our site. 45 | 46 | In case you are combining different hardware 47 | together (e.g. ESP32 + Display + touch screen) 48 | and there is no board name, then please define 49 | a name combining the different hardware together. 50 | 51 | For example: 52 | 53 | ESP32_ILI9431_XPT2046_240x320_custom.h 54 | 55 | This says that you are using an ESP32 56 | together with a display of type ILI9431 57 | and a touch screen of type XPT2046 with 58 | a resolution of 240x320 pixels. 59 | 60 | Please follow the same order on the description. 61 | This helps to be consistent. Some displays use 62 | RGB, others use BGR for colors. Put this also 63 | on the file name when existing different versions. 64 | 65 | 66 | _ 67 | _( )_ 68 | (_(0)_) 69 | (_) Can you help others getting their board working? 70 | | __ 71 | |/_/ Leave a line with your contact below 72 | | and commit an update to this file. 73 | | 74 | 75 | [2023] Max Brito https://github.com/maxbrito500 76 | 77 | 78 | */ 79 | #endif 80 | -------------------------------------------------------------------------------- /src/hardware/display/tft_st7796.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/hardware.h" 2 | 3 | #ifdef ST7796 4 | 5 | #define CMD_SWRESET 0x01 // Software Reset 6 | #define CMD_SLPIN 0x10 // Sleep in 7 | #define CMD_SLPOUT 0x11 // Sleep out 8 | #define CMD_NORON 0x13 // Normal Display Mode On 9 | #define CMD_INVOFF 0x20 // Display Inversion Off 10 | #define CMD_DISPON 0x29 // Display On 11 | #define CMD_CASET 0x2A // Column Address Set 12 | #define CMD_RASET 0x2B // Row Address Set 13 | #define CMD_RAMWR 0x2C // Memory Write 14 | #define CMD_MADCTL 0x36 // Memory Data Access Control 15 | #define CMD_COLMOD 0x3A // Interface Pixel Format 16 | #define CMD_PGC 0E0 // Positive Gamma Control 17 | #define CMD_NGC 0xE1 // Negative Gamma Control 18 | #define CMD_CSCON 0xF0 // Command Set Control 19 | 20 | #define MADCTL_MY 0x80 // Row Address Order - 0=Increment (Top to Bottom), 1=Decrement (Bottom to Top) 21 | #define MADCTL_MX 0x40 // Column Address Order - 0=Increment (Left to Right), 1=Decrement (Right to Left) 22 | #define MADCTL_MV 0x20 // Row/Column exchange - 0=Normal, 1=Row/Column exchanged 23 | #define MADCTL_ML 0x10 // Vertical Refresh Order 24 | #define MADCTL_BGR 0x08 // RGB/BGR Order - BGR 25 | #define MADCTL_MH 0x10 // Horizontal Refresh Order 26 | #define MADCTL_RGB 0x00 // RGB/BGR Order - RGB 27 | 28 | #define COLMOD_RGB_16BIT 0x50 29 | #define COLMOD_CTRL_16BIT 0x05 30 | #define COLMOD_RGB656 (COLMOD_RGB_16BIT | COLMOD_CTRL_16BIT) 31 | 32 | #if !defined(TFT_ORIENTATION_PORTRAIT) && !defined(TFT_ORIENTATION_LANDSCAPE) && !defined(TFT_ORIENTATION_PORTRAIT_INV) && !defined(TFT_ORIENTATION_LANDSCAPE_INV) 33 | #error Please define orientation: TFT_ORIENTATION_PORTRAIT, TFT_ORIENTATION_LANDSCAPE, TFT_ORIENTATION_PORTRAIT_INV or TFT_ORIENTATION_LANDSCAPE_INV 34 | #endif 35 | 36 | #if !defined(TFT_PANEL_ORDER_RGB) && !defined(TFT_PANEL_ORDER_BGR) 37 | #error Please define RGB order: TFT_PANEL_ORDER_BGR or 38 | #endif 39 | 40 | #ifdef TFT_PANEL_ORDER_RGB 41 | #define MADCTL_PANEL_ORDER MADCTL_RGB 42 | #else 43 | #ifdef TFT_PANEL_ORDER_BGR 44 | #define MADCTL_PANEL_ORDER MADCTL_BGR 45 | #else 46 | #error TFT_PANEL_ORDER not defined! 47 | #endif 48 | #endif 49 | 50 | void st7796_send_command(const uint8_t command, const uint8_t data[] = nullptr, const ushort length = 0) 51 | { 52 | digitalWrite(ST7796_PIN_DC, LOW); // Command mode => command 53 | spi_st7796.beginTransaction(SPISettings(ST7796_SPI_FREQ, MSBFIRST, SPI_MODE0)); 54 | digitalWrite(ST7796_PIN_CS, LOW); // Chip select => enable 55 | spi_st7796.write(command); 56 | if (length > 0) 57 | { 58 | digitalWrite(ST7796_PIN_DC, HIGH); // Command mode => data 59 | spi_st7796.writeBytes(data, length); 60 | } 61 | digitalWrite(ST7796_PIN_CS, HIGH); // Chip select => disable 62 | spi_st7796.endTransaction(); 63 | } 64 | 65 | void st7796_send_pixels(const uint8_t command, const lv_color_t data[], const ushort length) 66 | { 67 | digitalWrite(ST7796_PIN_DC, LOW); // Command mode => command 68 | spi_st7796.beginTransaction(SPISettings(ST7796_SPI_FREQ, MSBFIRST, SPI_MODE0)); 69 | digitalWrite(ST7796_PIN_CS, LOW); // Chip select => enable 70 | spi_st7796.write(command); 71 | if (length > 0) 72 | { 73 | digitalWrite(ST7796_PIN_DC, HIGH); // Command mode => data 74 | spi_st7796.writePixels(data, sizeof(lv_color_t) * length); 75 | } 76 | digitalWrite(ST7796_PIN_CS, HIGH); // Chip select => disable 77 | spi_st7796.endTransaction(); 78 | } 79 | 80 | void st7796_send_init_commands() 81 | { 82 | st7796_send_command(CMD_SWRESET); // Software reset 83 | delay(100); 84 | 85 | static const uint8_t cscon1[] = {0xC3}; // Enable extension command 2 part I 86 | st7796_send_command(CMD_CSCON, cscon1, sizeof(cscon1)); 87 | static const uint8_t cscon2[] = {0x96}; // Enable extension command 2 part II 88 | st7796_send_command(CMD_CSCON, cscon2, sizeof(cscon2)); 89 | 90 | static const uint8_t colmod[] = {COLMOD_RGB656}; // 16 bits R5G6B5 91 | st7796_send_command(CMD_COLMOD, colmod, sizeof(colmod)); // Set color mode 92 | 93 | #ifdef TFT_ORIENTATION_PORTRAIT 94 | static const uint8_t madctl[] = {MADCTL_MY | MADCTL_PANEL_ORDER}; // Portrait 0 Degrees 95 | #else 96 | #ifdef TFT_ORIENTATION_LANDSCAPE 97 | static const uint8_t madctl[] = {MADCTL_MV | MADCTL_PANEL_ORDER}; // Landscape 90 Degrees 98 | #else 99 | #ifdef TFT_ORIENTATION_PORTRAIT_INV 100 | static const uint8_t madctl[] = {MADCTL_MX | MADCTL_PANEL_ORDER}; // Portrait inverted 180 Degrees 101 | #else 102 | #ifdef TFT_ORIENTATION_LANDSCAPE_INV 103 | static const uint8_t madctl[] = {MADCTL_MY | MADCTL_MX | MADCTL_MV | MADCTL_PANEL_ORDER}; // Landscape inverted 270 Degrees 104 | #else 105 | #error TFT_ORIENTATION not defined! 106 | #endif 107 | #endif 108 | #endif 109 | #endif 110 | st7796_send_command(CMD_MADCTL, madctl, sizeof(madctl)); 111 | 112 | static const uint8_t pgc[] = {0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B}; 113 | st7796_send_command(CMD_PGC, pgc, sizeof(pgc)); 114 | static const uint8_t ngc[] = {0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B}; 115 | st7796_send_command(CMD_NGC, ngc, sizeof(ngc)); 116 | 117 | static const uint8_t cscon3[] = {0x3C}; // Disable extension command 2 part I 118 | st7796_send_command(CMD_CSCON, cscon3, sizeof(cscon3)); 119 | static const uint8_t cscon4[] = {0x69}; // Disable extension command 2 part II 120 | st7796_send_command(CMD_CSCON, cscon4, sizeof(cscon4)); 121 | 122 | st7796_send_command(CMD_INVOFF); // Inversion off 123 | st7796_send_command(CMD_NORON); // Normal display on 124 | st7796_send_command(CMD_SLPOUT); // Out of sleep mode 125 | st7796_send_command(CMD_DISPON); // Main screen turn on 126 | } 127 | 128 | void lvgl_tft_init() 129 | { 130 | pinMode(ST7796_PIN_DC, OUTPUT); // Data or Command 131 | pinMode(ST7796_PIN_CS, OUTPUT); // Chip Select 132 | digitalWrite(ST7796_PIN_CS, HIGH); 133 | 134 | pinMode(ST7796_PIN_BL, OUTPUT); // Backlight 135 | ledcSetup(ST7796_PWM_CHANNEL_BL, ST7796_PWM_FREQ_BL, ST7796_PWM_BITS_BL); 136 | ledcAttachPin(ST7796_PIN_BL, ST7796_PWM_CHANNEL_BL); 137 | 138 | st7796_send_init_commands(); 139 | 140 | smartdisplay_tft_set_backlight(ST7796_PWM_MAX_BL); // Backlight on 141 | } 142 | 143 | void lvgl_tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 144 | { 145 | // Column addresses 146 | const uint8_t caset[] = { 147 | static_cast(area->x1 >> 8), 148 | static_cast(area->x1), 149 | static_cast(area->x2 >> 8), 150 | static_cast(area->x2)}; 151 | st7796_send_command(CMD_CASET, caset, sizeof(caset)); 152 | // Page addresses 153 | const uint8_t raset[] = { 154 | static_cast(area->y1 >> 8), 155 | static_cast(area->y1), 156 | static_cast(area->y2 >> 8), 157 | static_cast(area->y2)}; 158 | st7796_send_command(CMD_RASET, raset, sizeof(raset)); 159 | // Memory write 160 | const auto size = lv_area_get_width(area) * lv_area_get_height(area); 161 | st7796_send_pixels(CMD_RAMWR, color_map, size); 162 | lv_disp_flush_ready(drv); 163 | } 164 | 165 | void smartdisplay_tft_set_backlight(uint16_t duty) 166 | { 167 | ledcWrite(ST7796_PWM_CHANNEL_BL, duty); 168 | } 169 | 170 | void smartdisplay_tft_sleep() 171 | { 172 | static const uint8_t slpin[] = {0x08}; 173 | st7796_send_command(CMD_SLPIN, slpin, sizeof(slpin)); 174 | } 175 | 176 | void smartdisplay_tft_wake() 177 | { 178 | static const uint8_t splout[] = {0x08}; 179 | st7796_send_command(CMD_SLPOUT, splout, sizeof(splout)); 180 | } 181 | 182 | #endif -------------------------------------------------------------------------------- /src/hardware/hardware.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware.h" 2 | #include 3 | 4 | std::recursive_mutex lvgl_mutex; 5 | 6 | 7 | // Functions to be defined in the tft driver 8 | extern void lvgl_tft_init(); 9 | extern void lvgl_tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); 10 | 11 | // Functions to be defined in the touch driver 12 | extern void lvgl_touch_init(); 13 | extern void lvgl_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data); 14 | 15 | 16 | /** 17 | * Each hardware needs its driver initialized here 18 | */ 19 | 20 | #ifdef ILI9431 21 | SPIClass spi_ili9431; 22 | #endif 23 | 24 | #ifdef XPT2046 25 | SPIClass spi_xpt2046; 26 | #endif 27 | 28 | #ifdef ST7796 29 | SPIClass spi_st7796; 30 | #endif 31 | 32 | #ifdef GT911 33 | TwoWire i2c_gt911 = TwoWire(1); // Bus number 1 34 | #endif 35 | 36 | 37 | #if LV_USE_LOG 38 | void lvgl_log(const char *buf) 39 | { 40 | log_printf("%s", buf); 41 | } 42 | #endif 43 | 44 | void smartdisplay_init() 45 | { 46 | // Lock access to LVGL 47 | const std::lock_guard lock(lvgl_mutex); 48 | // Setup RGB LED. High is off 49 | // Use channel 0=R, 1=G, 2=B, 5kHz, 8 bit resolution 50 | pinMode(LED_PIN_R, OUTPUT); 51 | digitalWrite(LED_PIN_R, true); 52 | ledcSetup(LED_PWM_CHANNEL_R, LED_PWM_FREQ, LED_PWM_BITS); 53 | ledcAttachPin(LED_PIN_R, LED_PWM_CHANNEL_R); 54 | 55 | pinMode(LED_PIN_G, OUTPUT); 56 | digitalWrite(LED_PIN_G, true); 57 | ledcSetup(LED_PWM_CHANNEL_G, LED_PWM_FREQ, LED_PWM_BITS); 58 | ledcAttachPin(LED_PIN_G, LED_PWM_CHANNEL_G); 59 | 60 | pinMode(LED_PIN_B, OUTPUT); 61 | digitalWrite(LED_PIN_B, true); 62 | ledcSetup(LED_PWM_CHANNEL_B, LED_PWM_FREQ, LED_PWM_BITS); 63 | ledcAttachPin(LED_PIN_B, LED_PWM_CHANNEL_B); 64 | 65 | // Setup CDS Light sensor 66 | analogSetAttenuation(ADC_0db); // 0dB(1.0x) 0~800mV 67 | pinMode(CDS_PIN, INPUT); 68 | 69 | // Audio 70 | pinMode(AUDIO_PIN, INPUT); // Set high impedance 71 | 72 | /* 73 | #if LV_USE_LOG 74 | lv_log_register_print_cb(lvgl_log); 75 | #endif 76 | */ 77 | lv_init(); 78 | 79 | // Setup interfaces 80 | setupHardwareInterfaces(); 81 | 82 | 83 | // Setup TFT display 84 | lvgl_tft_init(); 85 | static lv_disp_draw_buf_t draw_buf; 86 | static lv_color_t buf[TFT_WIDTH * 10]; 87 | lv_disp_draw_buf_init(&draw_buf, buf, NULL, TFT_WIDTH * 10); 88 | 89 | // Setup TFT display 90 | static lv_disp_drv_t disp_drv; 91 | lv_disp_drv_init(&disp_drv); 92 | #if defined(TFT_ORIENTATION_PORTRAIT) || defined(TFT_ORIENTATION_PORTRAIT_INV) 93 | disp_drv.hor_res = TFT_WIDTH; 94 | disp_drv.ver_res = TFT_HEIGHT; 95 | #else 96 | #if defined(TFT_ORIENTATION_LANDSCAPE) || defined(TFT_ORIENTATION_LANDSCAPE_INV) 97 | disp_drv.hor_res = TFT_HEIGHT; 98 | disp_drv.ver_res = TFT_WIDTH; 99 | #else 100 | #error TFT_ORIENTATION not defined! 101 | #endif 102 | #endif 103 | disp_drv.flush_cb = lvgl_tft_flush; 104 | disp_drv.draw_buf = &draw_buf; 105 | lv_disp_drv_register(&disp_drv); 106 | 107 | // Clear screen 108 | lv_obj_clean(lv_scr_act()); 109 | 110 | // Setup touch 111 | lvgl_touch_init(); 112 | static lv_indev_drv_t indev_drv; 113 | lv_indev_drv_init(&indev_drv); 114 | indev_drv.type = LV_INDEV_TYPE_POINTER; 115 | indev_drv.read_cb = lvgl_touch_read; 116 | lv_indev_drv_register(&indev_drv); 117 | } 118 | 119 | void smartdisplay_set_led_color(lv_color32_t rgb) 120 | { 121 | ledcWrite(LED_PWM_CHANNEL_R, LED_PWM_MAX - rgb.ch.red); 122 | ledcWrite(LED_PWM_CHANNEL_G, LED_PWM_MAX - rgb.ch.green); 123 | ledcWrite(LED_PWM_CHANNEL_B, LED_PWM_MAX - rgb.ch.blue); 124 | } 125 | 126 | int smartdisplay_get_light_intensity() 127 | { 128 | return analogRead(CDS_PIN); 129 | } 130 | 131 | void smartdisplay_beep(unsigned int frequency, unsigned long duration) 132 | { 133 | // Uses PWM Channel 0 134 | tone(AUDIO_PIN, frequency, duration); 135 | } 136 | -------------------------------------------------------------------------------- /src/hardware/hardware.h: -------------------------------------------------------------------------------- 1 | #ifndef HARDWARE_H 2 | #define HARDWARE_H 3 | 4 | #include 5 | #include 6 | #include 7 | //#include "bluetooth/bluetooth.cpp" 8 | #include "bluetooth/TerminalBluetooth.cpp" 9 | #include "wifi/module_wifi.hpp" 10 | 11 | 12 | 13 | #include 14 | 15 | // Mutex to access lvgl if multi-threaded 16 | extern std::recursive_mutex lvgl_mutex; 17 | // Initialize the display and touch 18 | extern void smartdisplay_init(); 19 | // Set the color of the led 20 | extern void smartdisplay_set_led_color(lv_color32_t rgb); 21 | // Get the value of the CDS sensor 22 | extern int smartdisplay_get_light_intensity(); 23 | // Beep with the specified frequency and duration 24 | extern void smartdisplay_beep(unsigned int frequency, unsigned long duration); 25 | // Set the brightness of the backlight display 26 | extern void smartdisplay_tft_set_backlight(uint16_t duty); // 0-1023 (12 bits) 27 | // Put the display to sleep 28 | extern void smartdisplay_tft_sleep(); 29 | // Wake the display 30 | extern void smartdisplay_tft_wake(); 31 | 32 | #include "choose.h" 33 | 34 | #endif -------------------------------------------------------------------------------- /src/hardware/touch/touch_gt911.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/hardware.h" 2 | 3 | #ifdef GT911 4 | 5 | #define GT911_I2C_SLAVE_ADDR 0x5D 6 | #define GT911_MAX_CONTACTS 5 7 | 8 | #define GT911_PRODUCT_ID1 0x8140 9 | #define GT911_REG_COORD_ADDR 0x814E 10 | #define GT911_TRACK_ID1 0x814F 11 | 12 | #define GT911_PRODUCT_ID_LEN 4 13 | 14 | struct __attribute__((packed)) GTPoint 15 | { 16 | // 0x814F-0x8156, ... 0x8176 (5 points) 17 | uint8_t trackId; 18 | uint16_t x; 19 | uint16_t y; 20 | uint16_t area; 21 | uint8_t reserved; 22 | }; 23 | 24 | #if !defined(TFT_ORIENTATION_PORTRAIT) && !defined(TFT_ORIENTATION_LANDSCAPE) && !defined(TFT_ORIENTATION_PORTRAIT_INV) && !defined(TFT_ORIENTATION_LANDSCAPE_INV) 25 | #error Please define orientation: TFT_ORIENTATION_PORTRAIT, TFT_ORIENTATION_LANDSCAPE, TFT_ORIENTATION_PORTRAIT_INV or TFT_ORIENTATION_LANDSCAPE_INV 26 | #endif 27 | 28 | bool gt911_write_register(uint16_t reg, const uint8_t buf[], int len) 29 | { 30 | i2c_gt911.beginTransmission(GT911_I2C_SLAVE_ADDR); 31 | if (!i2c_gt911.write(reg >> 8) || !i2c_gt911.write(reg & 0xFF)) 32 | return false; 33 | 34 | auto sent = i2c_gt911.write(buf, len); 35 | i2c_gt911.endTransmission(); 36 | return sent == len; 37 | } 38 | 39 | bool gt911_read_register(uint16_t reg, uint8_t buf[], int len) 40 | { 41 | i2c_gt911.beginTransmission(GT911_I2C_SLAVE_ADDR); 42 | if (!i2c_gt911.write(reg >> 8) || !i2c_gt911.write(reg & 0xFF)) 43 | return false; 44 | 45 | i2c_gt911.endTransmission(false); 46 | auto requested = i2c_gt911.requestFrom(GT911_I2C_SLAVE_ADDR, len); 47 | if (requested != len) 48 | return false; 49 | 50 | while (i2c_gt911.available() && len--) 51 | *buf++ = i2c_gt911.read(); 52 | 53 | return len == 0; 54 | } 55 | 56 | int8_t gt911_num_points_available() 57 | { 58 | uint8_t coord_addr; 59 | if (!gt911_read_register(GT911_REG_COORD_ADDR, &coord_addr, sizeof(coord_addr))) 60 | { 61 | log_e("Unable to read COORD_ADDR register"); 62 | return 0; 63 | } 64 | 65 | if ((coord_addr & 0x80) && ((coord_addr & 0x0F) < GT911_MAX_CONTACTS)) 66 | { 67 | uint8_t zero = 0; 68 | if (!gt911_write_register(GT911_REG_COORD_ADDR, &zero, sizeof(zero))) 69 | { 70 | log_e("Unable to reset COORD_ADDR register"); 71 | return 0; 72 | } 73 | 74 | return coord_addr & 0x0F; 75 | } 76 | 77 | return 0; 78 | } 79 | 80 | bool gt911_read_touches(GTPoint *points, uint8_t numPoints = GT911_MAX_CONTACTS) 81 | { 82 | if (!gt911_read_register(GT911_TRACK_ID1, (uint8_t *)points, sizeof(GTPoint) * numPoints)) 83 | { 84 | log_e("Unable to read GTPoints"); 85 | return false; 86 | } 87 | 88 | #ifdef TFT_ORIENTATION_PORTRAIT 89 | for (uint8_t i = 0; i < numPoints; ++i) 90 | { 91 | points[i].x = TFT_WIDTH - points[i].x; 92 | points[i].y = TFT_HEIGHT - points[i].y; 93 | } 94 | #else 95 | #ifdef TFT_ORIENTATION_LANDSCAPE 96 | uint16_t swap; 97 | for (uint8_t i = 0; i < numPoints; ++i) 98 | { 99 | swap = points[i].x; 100 | points[i].x = points[i].y; 101 | points[i].y = TFT_WIDTH - swap; 102 | } 103 | #else 104 | #ifdef TFT_ORIENTATION_PORTRAIT_INV 105 | for (uint8_t i = 0; i < numPoints; ++i) 106 | { 107 | points[i].x = points[i].x; 108 | points[i].y = points[i].y; 109 | } 110 | #else 111 | #ifdef TFT_ORIENTATION_LANDSCAPE_INV 112 | uint16_t swap; 113 | for (uint8_t i = 0; i < numPoints; ++i) 114 | { 115 | swap = points[i].x; 116 | points[i].x = TFT_HEIGHT - points[i].y; 117 | points[i].y = swap; 118 | } 119 | #else 120 | #error TFT_ORIENTATION not defined! 121 | #endif 122 | #endif 123 | #endif 124 | #endif 125 | return true; 126 | } 127 | 128 | void lvgl_touch_init() 129 | { 130 | uint8_t productId[GT911_PRODUCT_ID_LEN]; 131 | if (!gt911_read_register(GT911_PRODUCT_ID1, productId, GT911_PRODUCT_ID_LEN)) 132 | { 133 | log_e("No GT911 touch device found"); 134 | return; 135 | } 136 | 137 | log_i("DeviceId: %s", productId); 138 | } 139 | 140 | void lvgl_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data) 141 | { 142 | static int16_t last_x = 0, last_y = 0; 143 | // Ignore multi-touch 144 | auto points_available = gt911_num_points_available(); 145 | if (points_available == 1) 146 | { 147 | log_d("Touches detected: %d", points_available); 148 | GTPoint point; 149 | if (gt911_read_touches(&point, 1)) 150 | { 151 | log_d("Touch: (%d,%d)", point.x, point.y); 152 | data->state = LV_INDEV_STATE_PR; 153 | last_x = data->point.x = point.x; 154 | last_y = data->point.y = point.y; 155 | } 156 | } 157 | else 158 | { 159 | data->point.x = last_x; 160 | data->point.y = last_y; 161 | data->state = LV_INDEV_STATE_REL; 162 | } 163 | } 164 | 165 | #endif -------------------------------------------------------------------------------- /src/hardware/touch/touch_xpt2046.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/hardware.h" 2 | 3 | #ifdef XPT2046 4 | 5 | #define CMD_START_Z1_CONVERSION 0xB1 6 | #define CMD_START_Z2_CONVERSION 0xC1 7 | #define CMD_START_X_CONVERSION 0xD1 8 | #define CMD_START_Y_CONVERSION 0x91 9 | #define Z_THRESHOLD 600 10 | 11 | #if !defined(TFT_ORIENTATION_PORTRAIT) && !defined(TFT_ORIENTATION_LANDSCAPE) && !defined(TFT_ORIENTATION_PORTRAIT_INV) && !defined(TFT_ORIENTATION_LANDSCAPE_INV) 12 | #error Please define orientation: TFT_ORIENTATION_PORTRAIT, TFT_ORIENTATION_LANDSCAPE, TFT_ORIENTATION_PORTRAIT_INV or TFT_ORIENTATION_LANDSCAPE_INV 13 | #endif 14 | 15 | bool xpt2046_read_xy(int16_t *x, int16_t *y) 16 | { 17 | spi_xpt2046.beginTransaction(SPISettings(XPT2046_SPI_FREQ, MSBFIRST, SPI_MODE0)); 18 | digitalWrite(XPT2046_PIN_CS, LOW); 19 | spi_xpt2046.transfer16(CMD_START_Z1_CONVERSION); 20 | auto z1 = spi_xpt2046.transfer16(CMD_START_Z2_CONVERSION) >> 3; 21 | auto z2 = spi_xpt2046.transfer16(CMD_START_X_CONVERSION) >> 3; 22 | auto raw_x = spi_xpt2046.transfer16(CMD_START_Y_CONVERSION) >> 3; // Normalize to 12 bits 23 | auto raw_y = spi_xpt2046.transfer16(0) >> 3; // Normalize to 12 bits 24 | digitalWrite(XPT2046_PIN_CS, HIGH); 25 | spi_xpt2046.endTransaction(); 26 | int16_t z = z1 + 4095 - z2; 27 | if (z < Z_THRESHOLD) 28 | return false; 29 | 30 | #if 0 // For calibration 31 | static auto min_x = INT_MAX, max_x = -INT_MAX, min_y = INT_MAX, max_y = -INT_MAX; 32 | if (raw_x < min_x) 33 | min_x = raw_x; 34 | if (raw_x > max_x) 35 | max_x = raw_x; 36 | if (raw_y < min_y) 37 | min_y = raw_y; 38 | if (raw_y > max_y) 39 | max_y = raw_y; 40 | log_i("min_x=%d, max_x=%d, min_y=%d, max_y=%d", min_x, max_x, min_y, max_y); 41 | #endif 42 | 43 | #ifdef TFT_ORIENTATION_PORTRAIT 44 | *x = ((raw_x - XPT2046_MIN_X) * TFT_WIDTH) / (XPT2046_MAX_X - XPT2046_MIN_X); 45 | *y = TFT_HEIGHT - ((raw_y - XPT2046_MIN_Y) * TFT_HEIGHT) / (XPT2046_MAX_Y - XPT2046_MIN_Y); 46 | #else 47 | #ifdef TFT_ORIENTATION_LANDSCAPE 48 | *x = ((raw_y - XPT2046_MIN_Y) * TFT_HEIGHT) / (XPT2046_MAX_Y - XPT2046_MIN_Y); 49 | *y = ((raw_x - XPT2046_MIN_X) * TFT_WIDTH) / (XPT2046_MAX_X - XPT2046_MIN_X); 50 | #else 51 | #ifdef TFT_ORIENTATION_PORTRAIT_INV 52 | *x = TFT_WIDTH - ((raw_x - XPT2046_MIN_X) * TFT_WIDTH) / (XPT2046_MAX_X - XPT2046_MIN_X); 53 | *y = ((raw_y - XPT2046_MIN_Y) * TFT_HEIGHT) / (XPT2046_MAX_Y - XPT2046_MIN_Y); 54 | #else 55 | #ifdef TFT_ORIENTATION_LANDSCAPE_INV 56 | *x = TFT_HEIGHT - ((raw_y - XPT2046_MIN_Y) * TFT_HEIGHT) / (XPT2046_MAX_Y - XPT2046_MIN_Y); 57 | *y = TFT_WIDTH - ((raw_x - XPT2046_MIN_X) * TFT_WIDTH) / (XPT2046_MAX_X - XPT2046_MIN_X); 58 | #else 59 | #error TFT_ORIENTATION not defined! 60 | #endif 61 | #endif 62 | #endif 63 | #endif 64 | // log_i("x=%d,y=%d,raw_z=%d", *x, *y, z); 65 | return true; 66 | } 67 | 68 | void lvgl_touch_init() 69 | { 70 | pinMode(XPT2046_PIN_CS, OUTPUT); 71 | digitalWrite(XPT2046_PIN_CS, true); 72 | } 73 | 74 | void lvgl_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data) 75 | { 76 | static int16_t last_x = 0, last_y = 0; 77 | // log_d("Touch: (%d,%d)", last_x, last_y); 78 | data->state = xpt2046_read_xy(&last_x, &last_y) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_RELEASED; 79 | data->point.x = last_x; 80 | data->point.y = last_y; 81 | 82 | /* 83 | Serial.print("Touch: "); 84 | Serial.print(last_x); 85 | Serial.print(","); 86 | Serial.println(last_y); 87 | */ 88 | 89 | // log_d("Touch: (%d,%d)", last_x, last_y); 90 | } 91 | 92 | #endif -------------------------------------------------------------------------------- /src/hardware/wifi/blum_app_wifi.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUM_APP_WIFI_H 2 | #define BLUM_APP_WIFI_H 3 | 4 | #include "blum_app_wifi.cpp" 5 | 6 | #endif -------------------------------------------------------------------------------- /src/hardware/wifi/module_wifi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This project assumes that wifi modules are built inside 4 | the board such as the case for ESP32 models. 5 | 6 | Therefore provides no support for pin connections. 7 | 8 | */ 9 | 10 | #include 11 | #include "core/blum_global.h" 12 | #include "core/blum_status_bar.h" 13 | 14 | void wifiStop() { 15 | // remove the wifi icon 16 | iconWifiRemove(); 17 | // disconnect the wifi 18 | WiFi.disconnect(); 19 | 20 | // mark this setting in permanent memory 21 | preferences.begin(NAMESPACE_GENERIC, false); 22 | wifiEnabled = preferences.putBool(KEY_WIFI_ENABLED, false); 23 | preferences.end(); 24 | 25 | // mark this setting on runtime memory 26 | wifiEnabled = false; 27 | 28 | Serial.println("WiFi disconnected"); 29 | } 30 | 31 | boolean wifiStart() { 32 | Serial.println("Starting WiFi"); 33 | // remove the wifi icon at the beginning 34 | iconWifiRemove(); 35 | 36 | // read from flash memory 37 | preferences.begin(NAMESPACE_GENERIC, false); 38 | 39 | // there is no wifi to start 40 | if (wifiEnabled == false) { 41 | Serial.println("WiFi is disabled"); 42 | return false; 43 | } 44 | 45 | if(preferences.isKey(KEY_WIFI_ENABLED) == false){ 46 | Serial.println("There is no WiFi previously configured"); 47 | return false; 48 | } 49 | 50 | // preferences.putBool(KEY_WIFI_ENABLED, enabledWifi); 51 | // if wifi enabled, set it to true 52 | wifiEnabled = preferences.getBool(KEY_WIFI_ENABLED, false); 53 | 54 | // read the SSID and password 55 | Serial.println("Reading wifi SSID and password from flash memory"); 56 | wifi_ssid = preferences.getString(KEY_WIFI_SSID, ""); 57 | wifi_password = preferences.getString(KEY_WIFI_PASSWORD, ""); 58 | 59 | // no more need to read stuff from flash 60 | preferences.end(); 61 | 62 | 63 | 64 | if (wifi_ssid.isEmpty()) { 65 | Serial.println("WiFi needs to be configured, please connect to a reachable network"); 66 | return false; 67 | } 68 | 69 | if (wifi_password.isEmpty()) { 70 | Serial.println("WiFi password needs to be configured, please connect to a reachable network"); 71 | return false; 72 | } 73 | 74 | Serial.print("Connecting to WiFi: "); 75 | Serial.println(wifi_ssid); 76 | Serial.print("Password: "); 77 | Serial.println(wifi_password); 78 | 79 | WiFi.mode(WIFI_STA); 80 | WiFi.setSleep(WIFI_PS_NONE); 81 | WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str()); 82 | 83 | int maxTries = 5; 84 | int i = 0; 85 | 86 | while (WiFi.status() != WL_CONNECTED) { 87 | i = i + 1; 88 | delay(1000); 89 | Serial.print("."); 90 | if (i > maxTries) { 91 | Serial.println(""); 92 | Serial.println("WiFi connection failed"); 93 | return false; 94 | } 95 | } 96 | Serial.println("WiFi connected"); 97 | Serial.print("Device IP: "); 98 | Serial.print(WiFi.localIP()); 99 | Serial.print(" with terminal at port: "); 100 | Serial.println(SERVER_PORT); 101 | 102 | boolean isConnected = isWiFiConnected(); 103 | 104 | // create the icon on the status bar, only when connected 105 | if (isConnected) { 106 | iconWifiCreate(); 107 | wifiEnabled = true; 108 | }else{ 109 | wifiEnabled = false; 110 | } 111 | // are we really connected? 112 | return isConnected; 113 | } 114 | 115 | boolean isWiFiConnected() { 116 | return WiFi.status() == WL_CONNECTED; 117 | } -------------------------------------------------------------------------------- /src/hardware/wifi/module_wifi.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This project assumes that wifi modules are built inside 4 | the board such as the case for ESP32 models. 5 | 6 | Therefore provides no support for pin connections. 7 | 8 | */ 9 | 10 | #ifndef MODULE_WIFI_HPP 11 | #define MODULE_WIFI_HPP 12 | 13 | 14 | extern boolean wifiStart(); 15 | extern void wifiStop(); 16 | extern boolean isWiFiConnected(); 17 | 18 | #endif -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "blum.cpp" 4 | 5 | 6 | 7 | void setup(){ 8 | start(); 9 | } 10 | 11 | 12 | /** 13 | * Changes the builtin-LED colors in random colors 14 | */ 15 | static void buildLedColorLoop(){ 16 | 17 | auto r = (byte)(millis() / 75); 18 | auto g = (byte)(millis() / 10); 19 | auto b = (byte)(millis() / 150); 20 | 21 | smartdisplay_set_led_color(lv_color32_t({.ch = {.blue = b, .green = g, .red = r}})); 22 | } 23 | 24 | /** 25 | * Changes the display light intensity automatically 26 | * to reduce energy consumption and ease reading. 27 | */ 28 | static void adjustBrightnessAutomatically(){ 29 | // 0 means full day light 30 | // 2300 means full darkness 31 | int value = smartdisplay_get_light_intensity(); 32 | smartdisplay_tft_set_backlight(10); 33 | 34 | } 35 | 36 | 37 | static int64_t lastTime = esp_timer_get_time(); 38 | 39 | static void log(String title, int value){ 40 | Serial.print(title + ": "); 41 | Serial.println(value); 42 | } 43 | 44 | void loop(){ 45 | 46 | loopBlum(); 47 | 48 | // change the builtin led for fun 49 | buildLedColorLoop(); 50 | 51 | } -------------------------------------------------------------------------------- /src/utils/HashMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | 7 | Usage examples: 8 | 9 | HashMap* map = HashMapCreate(); 10 | 11 | // Add items to the map 12 | HashMapInsert(map, "apple", (void*)"A fruit"); 13 | 14 | // Get values from the map 15 | printf("%s\n", (char*)get(map, "apple")); // Output: A fruit 16 | printf("%s\n", (char*)get(map, "banana")); // Output: A fruit 17 | printf("%s\n", (char*)get(map, "carrot")); // Output: A vegetable 18 | 19 | // Remove items from the map 20 | removeKey(map, "banana"); 21 | 22 | // Check if an item exists 23 | printf("%d\n", containsKey(map, "apple")); // Output: 1 24 | printf("%d\n", containsKey(map, "banana")); // Output: 0 25 | 26 | // Iterate over all items in the map 27 | iterateHashMap(map); 28 | 29 | // Clear and destroy the map 30 | clearHashMap(map); 31 | destroyHashMap(map); 32 | 33 | */ 34 | 35 | 36 | // change the size as needed 37 | #define HASHSIZE 101 38 | 39 | typedef struct hashentry { 40 | const char* key; 41 | void* value; 42 | struct hashentry* next; 43 | } HashEntry; 44 | 45 | typedef struct hashmap { 46 | HashEntry* table[HASHSIZE]; 47 | } HashMap; 48 | 49 | static unsigned hash(const char* key) { 50 | unsigned hashval = 0; 51 | for (hashval = 0; *key != '\0'; key++) { 52 | hashval = *key + 31 * hashval; 53 | } 54 | return hashval % HASHSIZE; 55 | } 56 | 57 | static HashMap* HashMapCreate() { 58 | HashMap* map = (HashMap*)malloc(sizeof(HashMap)); 59 | memset(map->table, 0, sizeof(map->table)); 60 | return map; 61 | } 62 | 63 | static void HashMapInsert(HashMap* map, const char* key, void* value) { 64 | HashEntry* entry = (HashEntry*)malloc(sizeof(HashEntry)); 65 | entry->key = key; 66 | entry->value = value; 67 | unsigned hashval = hash(key); 68 | entry->next = map->table[hashval]; 69 | map->table[hashval] = entry; 70 | } 71 | 72 | static void* HashMapGet(HashMap* map, const char* key) { 73 | HashEntry* entry; 74 | for (entry = map->table[hash(key)]; entry != NULL; entry = entry->next) { 75 | if (strcmp(key, entry->key) == 0) { 76 | return entry->value; 77 | } 78 | } 79 | return NULL; 80 | } 81 | 82 | static void HashMapRemoveKey(HashMap* map, const char* key) { 83 | unsigned hashval = hash(key); 84 | HashEntry* prev = NULL; 85 | HashEntry* entry = map->table[hashval]; 86 | while (entry != NULL && strcmp(key, entry->key) != 0) { 87 | prev = entry; 88 | entry = entry->next; 89 | } 90 | if (entry == NULL) { 91 | return; 92 | } 93 | if (prev == NULL) { 94 | map->table[hashval] = entry->next; 95 | } else { 96 | prev->next = entry->next; 97 | } 98 | free(entry); 99 | } 100 | 101 | static int HashMapContainsKey(HashMap* map, const char* key) { 102 | unsigned hashval = hash(key); 103 | HashEntry* entry; 104 | for (entry = map->table[hashval]; entry != NULL; entry = entry->next) { 105 | if (strcmp(key, entry->key) == 0) { 106 | return 1; 107 | } 108 | } 109 | return 0; 110 | } 111 | 112 | static void HashMapClear(HashMap* map) { 113 | HashEntry* entry; 114 | HashEntry* temp; 115 | for (int i = 0; i < HASHSIZE; i++) { 116 | entry = map->table[i]; 117 | while (entry != NULL) { 118 | temp = entry; 119 | entry = entry->next; 120 | free(temp); 121 | } 122 | map->table[i] = NULL; 123 | } 124 | } 125 | 126 | static void HashMapDestroy(HashMap* map) { 127 | HashMapClear(map); 128 | free(map); 129 | } 130 | 131 | static void HashMapIterate(HashMap* map) { 132 | HashEntry* entry; 133 | for (int i = 0; i < HASHSIZE; i++) { 134 | for (entry = map->table[i]; entry != NULL; entry = entry->next) { 135 | printf("Key: %s, Value: %p\n", entry->key, entry->value); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/utils/StringArray.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "StringArray.h" 4 | 5 | static StringArray* StringArray_create(int capacity) { 6 | StringArray *stringArray = (StringArray*) malloc(sizeof(StringArray)); 7 | stringArray->strings = (const char**) malloc(capacity * sizeof(const char*)); 8 | stringArray->size = 0; 9 | stringArray->capacity = capacity; 10 | return stringArray; 11 | } 12 | 13 | static void StringArray_destroy(StringArray *stringArray) { 14 | for (int i = 0; i < stringArray->size; i++) { 15 | free((char*) stringArray->strings[i]); 16 | } 17 | free(stringArray->strings); 18 | free(stringArray); 19 | } 20 | 21 | static int StringArray_add(StringArray *stringArray, const char *str) { 22 | if (stringArray->size == stringArray->capacity) { 23 | stringArray->capacity *= 2; 24 | stringArray->strings = (const char**) realloc(stringArray->strings, stringArray->capacity * sizeof(const char*)); 25 | } 26 | char *newString = (char*) malloc(MAX_STR_LEN * sizeof(char)); 27 | strcpy(newString, str); 28 | stringArray->strings[stringArray->size] = (const char *) newString; 29 | stringArray->size++; 30 | return (stringArray->size - 1); 31 | } 32 | 33 | static const char* StringArray_get(StringArray *stringArray, int index) { 34 | if (index < 0 || index >= stringArray->size) { 35 | return NULL; 36 | } 37 | return stringArray->strings[index]; 38 | } 39 | 40 | static int StringArray_delete(StringArray *stringArray, int index) { 41 | if (index < 0 || index >= stringArray->size) { 42 | return 0; 43 | } 44 | free((char*) stringArray->strings[index]); 45 | for (int i = index; i < stringArray->size - 1; i++) { 46 | stringArray->strings[i] = stringArray->strings[i+1]; 47 | } 48 | stringArray->size--; 49 | return 1; 50 | } 51 | 52 | static int StringArray_size(StringArray *stringArray) { 53 | return stringArray->size; 54 | } 55 | -------------------------------------------------------------------------------- /src/utils/StringArray.h: -------------------------------------------------------------------------------- 1 | #ifndef STRINGARRAY_H 2 | #define STRINGARRAY_H 3 | 4 | #define MAX_STR_LEN 100 5 | 6 | /* 7 | 8 | 9 | 10 | StringArray *myStrings = StringArray_create(5); 11 | 12 | StringArray_add(myStrings, "Hello"); 13 | StringArray_add(myStrings, "World"); 14 | StringArray_add(myStrings, "How"); 15 | StringArray_add(myStrings, "Are"); 16 | StringArray_add(myStrings, "You?"); 17 | 18 | printf("Size of array: %d\n", StringArray_size(myStrings)); 19 | 20 | StringArray_delete(myStrings, 2); 21 | 22 | printf("Size of array after deleting element at index 2: %d\n", StringArray_size(myStrings)); 23 | 24 | StringArray_destroy(myStrings); 25 | 26 | */ 27 | 28 | typedef struct { 29 | const char **strings; 30 | int size; 31 | int capacity; 32 | } StringArray; 33 | 34 | /* 35 | * Function: StringArray_create 36 | * ---------------------------- 37 | * Creates a new StringArray with the specified capacity. 38 | * 39 | * capacity: The initial capacity of the array. 40 | * 41 | * returns: A pointer to the newly created StringArray. 42 | */ 43 | static StringArray* StringArray_create(int capacity); 44 | 45 | /* 46 | * Function: StringArray_destroy 47 | * ----------------------------- 48 | * Frees the memory allocated to the specified StringArray. 49 | * 50 | * stringArray: A pointer to the StringArray to be destroyed. 51 | */ 52 | static void StringArray_destroy(StringArray *stringArray); 53 | 54 | /* 55 | * Function: StringArray_add 56 | * ------------------------- 57 | * Adds a new string to the end of the specified StringArray. 58 | * 59 | * stringArray: A pointer to the StringArray to which the string will be added. 60 | * str: The string to be added to the array. 61 | * 62 | * returns: The index of the newly added string in the array. 63 | */ 64 | static int StringArray_add(StringArray *stringArray, const char *str); 65 | 66 | /* 67 | * Returns the string at the given index in the StringArray 68 | */ 69 | static const char* StringArray_get(StringArray *stringArray, int index); 70 | 71 | /* 72 | * Function: StringArray_delete 73 | * ---------------------------- 74 | * Deletes the string at the specified index from the specified StringArray. 75 | * 76 | * stringArray: A pointer to the StringArray from which the string will be deleted. 77 | * index: The index of the string to be deleted. 78 | * 79 | * returns: 1 if the deletion was successful, 0 otherwise. 80 | */ 81 | static int StringArray_delete(StringArray *stringArray, int index); 82 | 83 | /* 84 | * Function: StringArray_size 85 | * -------------------------- 86 | * Gets the number of strings in the specified StringArray. 87 | * 88 | * stringArray: A pointer to the StringArray whose size will be returned. 89 | * 90 | * returns: The number of strings in the array. 91 | */ 92 | static int StringArray_size(StringArray *stringArray); 93 | 94 | #endif /* STRINGARRAY_H */ -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "HashMap.cpp" 5 | #include "StringArray.cpp" 6 | 7 | #endif -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------