diff --git a/.github/workflows/package_main.yml b/.github/workflows/package_main.yml index 3f95cb5..f446745 100644 --- a/.github/workflows/package_main.yml +++ b/.github/workflows/package_main.yml @@ -63,9 +63,8 @@ jobs: run: | # zip up just the files we uploaded for the release zip_name="wireless-debug-display-${{ matrix.build.name }}_$(git describe --tags --dirty).zip" - cd ${{ matrix.build.path }} zip -r -j $zip_name build/*.bin build/*.elf build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/flasher_args.json build/flash_args - echo "artifact_path=${{ matrix.build.path }}/$zip_name" >> "$GITHUB_ENV" + echo "artifact_path=$zip_name" >> "$GITHUB_ENV" - name: Attach files to release uses: softprops/action-gh-release@v2 diff --git a/README.md b/README.md index 44c9604..ba8291b 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,32 @@ will print the data to a text log for display. https://github.com/esp-cpp/wireless-debug-display/assets/213467/f835922f-e17f-4f76-95ee-5d6585e84656 -## Configuration - -You'll need to configure the build using `idf.py set-target ` -and then `idf.py menuconfig` to then set the `Wireless Debug Display -Configuration` which allows you to set which hardware you want to run it on, as -well as the WiFi Access Point connection information (ssid/password). It also -allows customization of the port of the UDP server that the debug display is -running. - -## Use + +**Table of Contents** + +- [Wireless Debug Display](#wireless-debug-display) + - [Description](#description) + - [Use](#use) + - [Program](#program) + - [Configure](#configure) + - [Sending Data to the Display](#sending-data-to-the-display) + - [Commands](#commands) + - [Plotting](#plotting) + - [Logging](#logging) + - [Development](#development) + - [Environment](#environment) + - [Build and Flash](#build-and-flash) + - [Output](#output) + - [Console Logs:](#console-logs) + - [Python script:](#python-script) + - [ESP32-WROVER-KIT](#esp32-wrover-kit) + - [LILYGO T-DECK](#lilygo-t-deck) + - [ESP32-S3-BOX](#esp32-s3-box) + - [ESP32-S3-BOX-3](#esp32-s3-box-3) + + + +## Description This code receives string data from a UDP server. It will parse that string data and determine which of the following three types of data it is: @@ -44,6 +60,86 @@ discovery using mDNS. send messages or a file to the debug display. NOTE: zeroconf may not be installed / accessible within the python environment used by ESP-IDF. +## Use + +You must first program your hardware. Afterwards, you can configure it via a USB +connection using its built-in CLI. + +### Program + +Download the release `programmer` executable from the latest [releases +page](https://github.com/esp-cpp/wireless-debug-display/releases) for `windows`, +`macos`, or `linux` - depending on which computer you want to use to perform the +one-time programming. There are a few programmers pre-built for the +`ESP-BOX`, the `LilyGo T-Deck`, or the `ESP32-Wrover-Kit`. + +1. Download the programmer +2. Unzip it +3. Double click the `exe` (if windows), or open a terminal and execute it from + the command line `./wireless-debug-display-_programmer_v2.0.0_macos.bin`, + where hardware is one of `esp-box` or `t-deck` or `wrover-kit`. + +### Configure + +To configure it, simply connect it to your computer via USB and open the serial +port in a terminal (e.g. `screen`, `PuTTY`, etc.) at 115200 baud. Once there, +you can use it as you would any other CLI - and the `help` command will provide +info about the commands available. + +Any SSID/Password you set will be securely saved in the board's NVS, which is +managed by the ESP-IDF WiFi subsystem. + +![CleanShot 2025-06-25 at 09 42 28](https://github.com/user-attachments/assets/680f2dbc-e75a-4359-8e18-752df31c42bd) + +```console +sta> help +Commands available: + - help + This help message + - exit + Quit the session + - log + Set the log verbosity for the wifi sta. + - connect + Connect to a WiFi network with the given SSID and password. + - connect + Connect to a WiFi network with the given SSID and password. + - disconnect + Disconnect from the current WiFi network. + - ssid + Get the current SSID (Service Set Identifier) of the WiFi connection. + - rssi + Get the current RSSI (Received Signal Strength Indicator) of the WiFi connection. + - ip + Get the current IP address of the WiFi connection. + - connected + Check if the WiFi is connected. + - mac + Get the current MAC address of the WiFi connection. + - bssid + Get the current BSSID (MAC addressof the access point) of the WiFi connection. + - channel + Get the current WiFi channel of the connection. + - config + Get the current WiFi configuration. + - scan + Scan for available WiFi networks. + - memory + Display minimum free memory. + - switch_tab + Switch to the next tab in the display. + - clear_info + Clear the Info display. + - clear_plots + Clear the Plot display. + - clear_logs + Clear the Log display. + - push_data + Push data to the display. + - push_info + Push info to the display. +``` + ## Sending Data to the Display This display is designed to receive data from any other device on the network, @@ -97,7 +193,25 @@ All other text is treated as a log and written out to the log window. Note, we do not wrap lines, so any text that would go off the edge of the screen is simply not rendered. -## Build and Flash +## Development + +You'll need to configure the build using `idf.py set-target ` +and then `idf.py menuconfig` to then set the `Wireless Debug Display +Configuration` which allows you to set which hardware you want to run it on, as +well as the WiFi Access Point connection information (ssid/password). It also +allows customization of the port of the UDP server that the debug display is +running. + +### Environment + +This project is an ESP-IDF project, currently [ESP-IDF +v.5.4](https://github.com/espressif/esp-idf). + +For information about setting up `ESP-IDF v5.4`, please see [the official +ESP-IDF getting started +documentation](https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32s3/get-started/index.html). + +### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: diff --git a/components/gui/include/graph_window.hpp b/components/gui/include/graph_window.hpp index 1d1c56d..df1701d 100644 --- a/components/gui/include/graph_window.hpp +++ b/components/gui/include/graph_window.hpp @@ -18,6 +18,14 @@ class GraphWindow : public Window { void add_data(const std::string &plot_name, int new_data); void remove_plot(const std::string &plot_name); + lv_obj_t *get_lv_obj(void) { return wrapper_; } + + void invalidate() { + if (wrapper_) { + lv_obj_invalidate(wrapper_); + } + } + protected: lv_chart_series_t *create_plot(const std::string &plotName); lv_chart_series_t *get_plot(const std::string &plotName); diff --git a/components/gui/include/gui.hpp b/components/gui/include/gui.hpp index 0517b5f..ffbaa85 100644 --- a/components/gui/include/gui.hpp +++ b/components/gui/include/gui.hpp @@ -52,6 +52,9 @@ class Gui { void switch_tab(); + void clear_plots(); + void clear_logs(); + void push_data(const std::string &data); std::string pop_data(); diff --git a/components/gui/include/text_window.hpp b/components/gui/include/text_window.hpp index 264cb68..02b4f6a 100644 --- a/components/gui/include/text_window.hpp +++ b/components/gui/include/text_window.hpp @@ -14,6 +14,14 @@ class TextWindow : public Window { void clear_logs(void); void add_log(const std::string &log_text); + lv_obj_t *get_lv_obj(void) { return log_container_; } + + void invalidate() { + if (log_container_) { + lv_obj_invalidate(log_container_); + } + } + private: std::string log_text_{""}; lv_obj_t *log_container_{nullptr}; diff --git a/components/gui/src/graph_window.cpp b/components/gui/src/graph_window.cpp index 02ea67c..8e598b6 100644 --- a/components/gui/src/graph_window.cpp +++ b/components/gui/src/graph_window.cpp @@ -118,6 +118,8 @@ void GraphWindow::clear_plots(void) { while (lv_spangroup_get_child(legend_, 0)) { lv_spangroup_delete_span(legend_, lv_spangroup_get_child(legend_, 0)); } + // invalidate + invalidate(); } void GraphWindow::add_data(const std::string &plotName, int newData) { diff --git a/components/gui/src/gui.cpp b/components/gui/src/gui.cpp index 8749465..31ae7e6 100644 --- a/components/gui/src/gui.cpp +++ b/components/gui/src/gui.cpp @@ -62,6 +62,16 @@ void Gui::clear_info() { info_window_.clear_logs(); } +void Gui::clear_plots() { + std::lock_guard lk{mutex_}; + plot_window_.clear_plots(); +} + +void Gui::clear_logs() { + std::lock_guard lk{mutex_}; + log_window_.clear_logs(); +} + void Gui::add_info(const std::string &info) { std::lock_guard lk{mutex_}; info_window_.add_log(info); diff --git a/components/gui/src/text_window.cpp b/components/gui/src/text_window.cpp index 4767fcd..4662464 100644 --- a/components/gui/src/text_window.cpp +++ b/components/gui/src/text_window.cpp @@ -14,10 +14,12 @@ void TextWindow::init(lv_obj_t *parent, size_t width, size_t height) { } void TextWindow::clear_logs(void) { - // clear all the logs off the page - lv_obj_clean(log_container_); + // set the string to the display + lv_label_set_text(log_container_, ""); // now empty the string log_text_.clear(); + // invalidate + invalidate(); } void TextWindow::add_log(const std::string &log_text) { diff --git a/dependencies.lock b/dependencies.lock index 65f3b34..9ea3578 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,6 +1,6 @@ dependencies: espp/base_component: - component_hash: 01a3fc5a70fa56eb5ce37b4fbc94e7cb27402df0fb61327db872fb96f49f81bd + component_hash: fc113b37edc55d1600b56b26868ed669e8a9a251ffae3244b0df3dab10de81d9 dependencies: - name: espp/logger registry_url: https://components.espressif.com @@ -12,9 +12,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/base_peripheral: - component_hash: e956923883ac06ad40913dde3b29ae497e5a21ceabc42538a92c0adde423fdf9 + component_hash: 7a148558a3a7c59a17d55c6ba53d616be7441fac0d40fa962690e04cd9aaf4cb dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -26,9 +26,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/button: - component_hash: 032d53e90d3878b2770cd9ed30124e0ff6d10cf1bf4875d925fa1b601a6e1575 + component_hash: 0e52a7833341952ae655490a7e45331e44e7a90363864de4d33b273e314556a4 dependencies: - name: espp/interrupt registry_url: https://components.espressif.com @@ -40,9 +40,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/cli: - component_hash: ab575c9f33663874d9588a9922e4bb7881d287ae3e8933b1b80d4e41a557c83d + component_hash: d8a91a86c881787e4972c62c25819ca2c80361b206f411e2d1d740082c96eb76 dependencies: - name: espp/logger registry_url: https://components.espressif.com @@ -52,11 +52,11 @@ dependencies: require: private version: '>=5.0' source: - registry_url: https://components.espressif.com + registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/codec: - component_hash: 722fb02fcb5f2ee3b3d1fe79df64c4c44c9a31532699fbd58dd01e9886b4d958 + component_hash: 1250fd0ac921199e4929e5712a7ac0aa83764074e36e13ab7b71ef04d6cd0690 dependencies: - name: idf require: private @@ -64,9 +64,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/display: - component_hash: ef7145ac005a56d947f022b60f79edcb4bf3da6b8242b4ee584377b7844ac271 + component_hash: e0411412a1f350ba8291e31a7123365b38afe4c92c58ac1419f5903af3f59beb dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -90,9 +90,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/display_drivers: - component_hash: 220e917c6ae88e38355189c712cb894bb7f36cbd3a9f9858d2db76057da616fd + component_hash: cc181825fe9e68b4728602ecd0155e8f90a8733ebedc9ee04267543c979ac60d dependencies: - name: espp/display registry_url: https://components.espressif.com @@ -108,9 +108,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/esp-box: - component_hash: 5934079f2d3c828ef9e87126ccc88e5ca2b6c055f6e90c2d18f7d37ddf9cd562 + component_hash: 56224c7945c1f8c2650acf38d449faa9c30cb3b37dbba1baa575542e4204c89c dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -162,9 +162,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/format: - component_hash: a2a180d2603d6885c25babff12a1148eaafae3a2e439ae58d04dcb91eb4a6d11 + component_hash: 18b83f3075950153c3596cb42ba61abac9100504b395e5f3ac8a54df07dfe61d dependencies: - name: idf require: private @@ -172,9 +172,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/gt911: - component_hash: e9ede7fa4ef7c871d252f9ee5d0f31d0d8946c287286ac83d8b64c53500d0c8b + component_hash: cce5b9c3723a2088c1fd4b7600c32afbce2a1841f55652fcdfcd5e26c8dfa9a4 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -186,9 +186,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/i2c: - component_hash: 6bb49048c94d28416bab9cbaa6db44981a0cd9098375bf4c317d6825bae5456b + component_hash: 9ee1db266f94cb800eeed1d8c3193057c331c7e4be15210b44cfcc646df1bab2 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -208,9 +208,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/icm42607: - component_hash: c5c653c6423cdb890c845c6d8627b637b25457c132258cf5f0f98f65f3ff3a32 + component_hash: 329e81f13bdf0acb04557cea176d6e0bfc69e5458176aee3f439fc23ec7e2a69 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -226,9 +226,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/input_drivers: - component_hash: c41c77b36f47bc0f1696f522f233a44ab7abd13597e58af6912ada91473dc007 + component_hash: b5438ba7860c84e942e8ab3f0a808ed0f28098cd7a6a7f7829c595e50556a724 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -244,9 +244,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/interrupt: - component_hash: 0162f1b0be8c998ff62b1696f0d9d2df12f3713ebbcc40e19cad12999eb149f1 + component_hash: cc63676a263ff39e4c25cc5df3d0d2a8b21c6d4b0ec192198cd5b5bd5ee5b6c9 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -262,9 +262,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/led: - component_hash: a30ca9078b3b5fc30e3525106035becec2ed554551c348982b178474bdb6022c + component_hash: 7f7254e29e9ba127781b7b43e1cda8068fdc39bafa1df2c42a161985ee3e561e dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -280,9 +280,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/logger: - component_hash: 789aa33f59405fd285fc3e7ab9609a1a53b70985463a01c1338181485d7ae8f5 + component_hash: 0198cc129fe3315d6aa892e9d752913e7385cc4fa33b183b361da4c618b4845f dependencies: - name: espp/format registry_url: https://components.espressif.com @@ -294,9 +294,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/math: - component_hash: 9a937f71150b6828bdb291487b011e8c9dcbab89798384a5a7a17eb7e407fa2d + component_hash: 10a30342a8e1da18ade43b1d98c451aca4d095de8ff9f801b2de463225b520ad dependencies: - name: espp/format registry_url: https://components.espressif.com @@ -308,9 +308,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/monitor: - component_hash: 10ba241995a9a795f2a0cf83c931378367cd6bc156ed6ebfef962a715044b046 + component_hash: 23bb543dddace4b7880bf093fe3e2ea6aee3b7e9c82dd12a5a06893a4d465797 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -326,9 +326,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/nvs: - component_hash: ff7d5447ee6ccc5bbaef78bb17218e7fbb4e3c4e7941cc071289625fefc9b3b6 + component_hash: 69cfa3529eb8d335948d0cceb38d01475ac860fd90bd09580cb6e75f502ba142 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -340,9 +340,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/rtsp: - component_hash: 0ccf374b03ddcfa7b9a95c673b48d213bb59d0f690aadd3966d3fe431a3597dd + component_hash: 4d2f596eec49a35d0e5aad80298b09f929db58ee8ed706ea2299e4ecd744dafb dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -362,9 +362,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/socket: - component_hash: 1995ad784c94abd90fe1b7bf24677807a9537aa30f71fc877786a22a682b2912 + component_hash: f253cd1eaf9316a67850d31c6c4e0afd565246bf57cca4f8bff3cdec7936bd7f dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -380,55 +380,55 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/t-deck: - component_hash: bb1099c7cf9370fdfd90852479cd9b88d2621b540fdb280cb2671610cea94735 + component_hash: 9c8a10481a0bb2e4cda2e13e2704d6ba1f0245de8262c75fd9c635450ad755dd dependencies: - - name: espp/i2c + - name: espp/base_component registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/input_drivers + - name: espp/display registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/interrupt + - name: espp/display_drivers registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/t_keyboard + - name: espp/gt911 registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/task + - name: espp/i2c registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: idf - require: private - version: '>=5.0' - - name: espp/base_component + - name: espp/input_drivers registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/display + - name: espp/interrupt registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/display_drivers + - name: espp/t_keyboard registry_url: https://components.espressif.com require: private version: '>=1.0' - - name: espp/gt911 + - name: espp/task registry_url: https://components.espressif.com require: private version: '>=1.0' + - name: idf + require: private + version: '>=5.0' source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/t_keyboard: - component_hash: b9409e6ffdb3f09f4876d8f3007bc8177e61d84183479d4209a8df31b878717d + component_hash: 04a64b0b2cb2757b4b8e4406a141019d24def5f7840247a387b900829b48dfde dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -440,9 +440,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/task: - component_hash: e3cf0cb67c9103a5311105489de38537faf2728ee76c3744edbdc0ee8a542b21 + component_hash: ceb5607383e0585ab312ac508a3e3d5e3c4825524298284108249e555e5c2a44 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -454,9 +454,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espp/tt21100: - component_hash: eaf45fcba6bcb214b95fb82341f4a5bdfffc290657b4d597f30acfc6ddb94d6d + component_hash: c2b35c139016e8878dd5768ad63a71cef2c4ae1197052f6692a915a0ebbb633b dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -468,21 +468,25 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.7 + version: 1.0.11 espp/wifi: - component_hash: b864baee9d2c7edc95334d2a66a86acfbf159a12cae2539951c2075e28086766 + component_hash: d7f4969357036ac5d9f4a68eea0838e383ccfbaf2b0f6c9e40c5c8776c116749 dependencies: - name: espp/base_component registry_url: https://components.espressif.com require: private version: '>=1.0' + - name: espp/cli + registry_url: https://components.espressif.com + require: private + version: '>=1.0' - name: idf require: private version: '>=5.0' source: registry_url: https://components.espressif.com/ type: service - version: 1.0.7 + version: 1.0.11 espressif/mdns: component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5 dependencies: @@ -506,6 +510,7 @@ dependencies: version: 9.3.0 direct_dependencies: - espp/button +- espp/cli - espp/display - espp/esp-box - espp/monitor @@ -517,6 +522,6 @@ direct_dependencies: - espp/wifi - espressif/mdns - idf -manifest_hash: e3e6ec9b6a5e43df0b4944237880c6c166882e269ae4ebfff7a5cfe8b1509732 +manifest_hash: 4d4fd991012ae8abcd5e0e2ff680094885f3969b5bd95cb05515f674c75c0442 target: esp32s3 version: 2.0.0 diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 3bb4d5f..9e3b954 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -24,15 +24,15 @@ menu "Wireless Debug Display Configuration" config ESP_WIFI_SSID string "WiFi SSID" - default "myssid" + default "" help - SSID (network name) for the camera streamer to connect to. + SSID (network name) for the wireless debug display to connect to. config ESP_WIFI_PASSWORD string "WiFi Password" - default "mypassword" + default "" help - WiFi password (WPA or WPA2) for the camera streamer to use. + WiFi password (WPA or WPA2) for the wireless debug display to use. config ESP_MAXIMUM_RETRY int "Maximum retry" diff --git a/main/idf_component.yml b/main/idf_component.yml index bd2312a..0e5c82f 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -14,12 +14,13 @@ dependencies: espp/esp-box: version: '>=1.0' rules: - - if: "target in [esp32s3]" + - if: target in [esp32s3] espp/t-deck: version: '>=1.0' rules: - - if: "target in [esp32s3]" + - if: target in [esp32s3] espp/wrover-kit: version: '>=1.0' rules: - - if: "target in [esp32]" + - if: target in [esp32] + espp/cli: '>=1.0' diff --git a/main/main.cpp b/main/main.cpp index c3935dc..178c763 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -7,10 +7,16 @@ #if CONFIG_HARDWARE_WROVER_KIT #include "wrover-kit.hpp" +#define HAS_TOUCH 0 +using Hal = espp::WroverKit; #elif CONFIG_HARDWARE_BOX #include "esp-box.hpp" +#define HAS_TOUCH 1 +using Hal = espp::EspBox; #elif CONFIG_HARDWARE_TDECK #include "t-deck.hpp" +#define HAS_TOUCH 1 +using Hal = espp::TDeck; #else #error "Misconfigured hardware!" #endif @@ -20,17 +26,35 @@ #endif #include "button.hpp" +#include "cli.hpp" #include "gui.hpp" #include "logger.hpp" #include "task.hpp" #include "tcp_socket.hpp" #include "udp_socket.hpp" #include "wifi_sta.hpp" +#include "wifi_sta_menu.hpp" using namespace std::chrono_literals; +static espp::Logger logger({.tag = "WirelessDebugDisplay", .level = espp::Logger::Verbosity::INFO}); + +static std::recursive_mutex gui_mutex; +static std::shared_ptr gui; + +static constexpr size_t server_port = CONFIG_DEBUG_SERVER_PORT; +static std::recursive_mutex server_mutex; +static std::unique_ptr start_server_task; +static std::string server_address = ""; +static std::shared_ptr server_socket; + +static std::shared_ptr wifi_sta; + +bool start_server(std::mutex &m, std::condition_variable &cv, bool &task_notified); +std::optional> on_data_received(const std::vector &data, + const espp::Socket::Info &sender_info); + extern "C" void app_main(void) { - espp::Logger logger({.tag = "WirelessDebugDisplay", .level = espp::Logger::Verbosity::INFO}); logger.info("Bootup"); #if CONFIG_ESP32_WIFI_NVS_ENABLED @@ -41,15 +65,7 @@ extern "C" void app_main(void) { #endif // hardware specific configuration -#if CONFIG_HARDWARE_WROVER_KIT - auto &hal = espp::WroverKit::get(); -#endif -#if CONFIG_HARDWARE_BOX - auto &hal = espp::EspBox::get(); -#endif -#if CONFIG_HARDWARE_TDECK - auto &hal = espp::TDeck::get(); -#endif + auto &hal = Hal::get(); hal.set_log_level(espp::Logger::Verbosity::INFO); @@ -67,15 +83,20 @@ extern "C" void app_main(void) { auto display = hal.display(); // create the gui - Gui gui(Gui::Config{.display = display, .log_level = espp::Logger::Verbosity::DEBUG}); + gui = std::make_shared( + Gui::Config{.display = display, .log_level = espp::Logger::Verbosity::DEBUG}); // initialize the input system -#if CONFIG_HARDWARE_WROVER_KIT +#if !HAS_TOUCH espp::Button button({ .interrupt_config = { .gpio_num = GPIO_NUM_0, - .callback = [&](const espp::Button::Event &event) { gui.switch_tab(); }, + .callback = + [](const espp::Button::Event &event) { + std::lock_guard lock(gui_mutex); + gui->switch_tab(); + }, .active_level = espp::Button::ActiveLevel::LOW, .interrupt_type = espp::Button::InterruptType::RISING_EDGE, .pullup_enabled = false, @@ -83,8 +104,7 @@ extern "C" void app_main(void) { }, .log_level = espp::Logger::Verbosity::WARN, }); -#endif -#if CONFIG_HARDWARE_BOX || CONFIG_HARDWARE_TDECK +#else if (!hal.initialize_touch()) { logger.error("Could not initialize touch"); return; @@ -93,88 +113,191 @@ extern "C" void app_main(void) { // initialize WiFi logger.info("Initializing WiFi"); - std::string server_address; - espp::WifiSta wifi_sta({.ssid = CONFIG_ESP_WIFI_SSID, - .password = CONFIG_ESP_WIFI_PASSWORD, - .num_connect_retries = CONFIG_ESP_MAXIMUM_RETRY, - .on_connected = nullptr, - .on_disconnected = nullptr, - .on_got_ip = [&server_address, &logger](ip_event_got_ip_t *eventdata) { - server_address = - fmt::format("{}.{}.{}.{}", IP2STR(&eventdata->ip_info.ip)); - logger.info("got IP: {}.{}.{}.{}", IP2STR(&eventdata->ip_info.ip)); - }}); - - // wait for network - while (!wifi_sta.is_connected()) { - logger.info("waiting for wifi connection..."); - std::this_thread::sleep_for(1s); - } + wifi_sta = std::make_shared(espp::WifiSta::Config{ + .ssid = CONFIG_ESP_WIFI_SSID, + .password = CONFIG_ESP_WIFI_PASSWORD, + .num_connect_retries = CONFIG_ESP_MAXIMUM_RETRY, + .on_connected = []() { logger.info("WiFi connected, waiting for IP"); }, + .on_disconnected = + []() { + logger.info("WiFi disconnected, stopping server task and freeing resources"); + std::lock_guard lock(server_mutex); + // stop the server task + start_server_task.reset(); + logger.info("server task stopped"); + // free the medns resources + mdns_free(); + logger.info("mdns resources freed"); + // delete the socket + server_socket.reset(); + logger.info("Socket resources freed"); + }, + .on_got_ip = + [](ip_event_got_ip_t *eventdata) { + server_address = fmt::format("{}.{}.{}.{}", IP2STR(&eventdata->ip_info.ip)); + logger.info("got IP: {}.{}.{}.{}", IP2STR(&eventdata->ip_info.ip)); + // update the info page + { + std::lock_guard lock(gui_mutex); + gui->clear_info(); + gui->add_info(std::string("#FF0000 WiFi: #") + wifi_sta->get_ssid()); + gui->add_info(std::string("#00FF00 IP: #") + server_address + ":" + + std::to_string(server_port)); + } + // start the server task + { + std::lock_guard lock(server_mutex); + start_server_task = espp::Task::make_unique( + espp::Task::Config{.callback = start_server, + .task_config = {.name = "Start Server Task", .priority = 10}}); + start_server_task->start(); + } + }}); + + espp::WifiStaMenu sta_menu(*wifi_sta); + auto root_menu = sta_menu.get(); + root_menu->Insert( + "memory", + [](std::ostream &out) { + out << "Minimum free memory: " << heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT) + << std::endl; + }, + "Display minimum free memory."); + + // add a command to switch tabs + root_menu->Insert( + "switch_tab", + [](std::ostream &out) { + std::lock_guard lock(gui_mutex); + gui->switch_tab(); + out << "Switched tab.\n"; + }, + "Switch to the next tab in the display."); + + // add a command to clear the info + root_menu->Insert( + "clear_info", + [](std::ostream &out) { + std::lock_guard lock(gui_mutex); + gui->clear_info(); + out << "Info cleared.\n"; + }, + "Clear the Info display."); + + // add a command to clear the plots + root_menu->Insert( + "clear_plots", + [](std::ostream &out) { + std::lock_guard lock(gui_mutex); + gui->clear_plots(); + out << "Plots cleared.\n"; + }, + "Clear the Plot display."); + + // add a command to clear the logs + root_menu->Insert( + "clear_logs", + [](std::ostream &out) { + std::lock_guard lock(gui_mutex); + gui->clear_logs(); + out << "Logs cleared.\n"; + }, + "Clear the Log display."); + + // add a command to push data into the display + root_menu->Insert("push_data", + [](std::ostream &out, const std::string &data) { + std::lock_guard lock(gui_mutex); + gui->push_data(data); + gui->handle_data(); + out << "Data pushed to display.\n"; + }, + "Push data to the display.", {"data"}); + + // add a command to push info into the display + root_menu->Insert("push_info", + [](std::ostream &out, const std::string &info) { + std::lock_guard lock(gui_mutex); + gui->add_info(info); + out << "Info pushed to display.\n"; + }, + "Push info to the display.", {"info"}); + cli::Cli cli(std::move(root_menu)); + cli::SetColor(); + cli.ExitAction([](auto &out) { out << "Goodbye and thanks for all the fish.\n"; }); + + espp::Cli input(cli); + input.SetInputHistorySize(10); + input.Start(); +} + +bool start_server(std::mutex &m, // cppcheck-suppress constParameterCallback + std::condition_variable &cv, // cppcheck-suppress constParameterCallback + bool &task_notified) { // cppcheck-suppress constParameterCallback // create the debug display socket - size_t server_port = CONFIG_DEBUG_SERVER_PORT; logger.info("Creating debug server at {}:{}", server_address, server_port); // create the socket - espp::UdpSocket server_socket({.log_level = espp::Logger::Verbosity::WARN}); - auto server_task_config = espp::Task::BaseConfig{ + server_socket = std::make_shared( + espp::UdpSocket::Config{.log_level = espp::Logger::Verbosity::WARN}); + espp::Task::BaseConfig server_task_config{ .name = "UdpServer", .stack_size_bytes = 6 * 1024, }; - auto server_config = espp::UdpSocket::ReceiveConfig{ + espp::UdpSocket::ReceiveConfig server_config{ .port = server_port, .buffer_size = 1024, - .on_receive_callback = [&gui]( - auto &data, auto &source) -> auto{// turn the vector into a string - std::string data_str(data.begin(), data.end()); - fmt::print("Server received: '{}'\n" - " from source: {}\n", - data_str, source); - gui.push_data(data_str); - gui.handle_data(); - return std::nullopt; -} -} -; + .on_receive_callback = on_data_received, + }; -server_socket.start_receiving(server_task_config, server_config); + // set a timeout on the socket, so that it doesn't block indefinitely. This is + // required to allow us to gracefully shutdown the socket. + server_socket->set_receive_timeout(1000ms); -// initialize mDNS, so that other embedded devices on the network can find us -// without having to be hardcoded / configured with our IP address and port -logger.info("Initializing mDNS"); -auto err = mdns_init(); -if (err != ESP_OK) { - logger.error("Could not initialize mDNS: {}", err); - return; -} + // now actually start the socket receiving task + server_socket->start_receiving(server_task_config, server_config); -uint8_t mac[6]; -esp_read_mac(mac, ESP_MAC_WIFI_STA); -std::string hostname = fmt::format("wireless-debug-display-{:x}{:x}{:x}", mac[3], mac[4], mac[5]); -err = mdns_hostname_set(hostname.c_str()); -if (err != ESP_OK) { - logger.error("Could not set mDNS hostname: {}", err); - return; -} -logger.info("mDNS hostname set to '{}'", hostname); -err = mdns_instance_name_set("Wireless Debug Display"); -if (err != ESP_OK) { - logger.error("Could not set mDNS instance name: {}", err); - return; -} -err = mdns_service_add("Wireless Debug Display", "_debugdisplay", "_udp", server_port, NULL, 0); -if (err != ESP_OK) { - logger.error("Could not add mDNS service: {}", err); - return; -} -logger.info("mDNS initialized"); + // initialize mDNS, so that other embedded devices on the network can find us + // without having to be hardcoded / configured with our IP address and port + logger.info("Initializing mDNS"); + auto err = mdns_init(); + if (err != ESP_OK) { + logger.error("Could not initialize mDNS: {}", err); + return true; // stop the task + } -// update the info page -gui.clear_info(); -gui.add_info(std::string("#FF0000 WiFi: #") + CONFIG_ESP_WIFI_SSID); -gui.add_info(std::string("#00FF00 IP: #") + server_address + ":" + std::to_string(server_port)); + uint8_t mac[6]; + esp_read_mac(mac, ESP_MAC_WIFI_STA); + std::string hostname = fmt::format("wireless-debug-display-{:x}{:x}{:x}", mac[3], mac[4], mac[5]); + err = mdns_hostname_set(hostname.c_str()); + if (err != ESP_OK) { + logger.error("Could not set mDNS hostname: {}", err); + return true; // stop the task + } + logger.info("mDNS hostname set to '{}'", hostname); + err = mdns_instance_name_set("Wireless Debug Display"); + if (err != ESP_OK) { + logger.error("Could not set mDNS instance name: {}", err); + return true; // stop the task + } + err = mdns_service_add("Wireless Debug Display", "_debugdisplay", "_udp", server_port, NULL, 0); + if (err != ESP_OK) { + logger.error("Could not add mDNS service: {}", err); + return true; // stop the task + } + logger.info("mDNS initialized"); -// loop forever -while (true) { - std::this_thread::sleep_for(1s); + return true; // stop the task } + +std::optional> on_data_received(const std::vector &data, + const espp::Socket::Info &sender_info) { + std::string data_str(data.begin(), data.end()); + fmt::print("Server received: '{}'\n" + " from source: {}\n", + data_str, sender_info); + std::lock_guard lock(gui_mutex); + gui->push_data(data_str); + gui->handle_data(); + return std::nullopt; } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index fe94567..1fe02d4 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -45,3 +45,6 @@ CONFIG_LV_USE_THEME_DEFAULT=y CONFIG_LV_THEME_DEFAULT_DARK=y CONFIG_LV_THEME_DEFAULT_GROW=y CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=80 + +# the cli library requires exceptions right now... +CONFIG_COMPILER_CXX_EXCEPTIONS=y