diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index 78e3b9e..d79bf3a 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -57,7 +57,7 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build - if: (github.event_name == 'release' && github.event.action == 'created') || (github.event_name != 'pull_request' && github.ref == 'refs/heads/master') + if: github.event_name == 'release' && github.event.action == 'created' steps: - name: Print GitHub event name run: | diff --git a/CYD-Klipper/src/conf/global_config.cpp b/CYD-Klipper/src/conf/global_config.cpp index cefc9d4..42cbf3c 100644 --- a/CYD-Klipper/src/conf/global_config.cpp +++ b/CYD-Klipper/src/conf/global_config.cpp @@ -3,6 +3,7 @@ #include "lvgl.h" GLOBAL_CONFIG global_config = {0}; +TEMPORARY_CONFIG temporary_config = {0}; COLOR_DEF color_defs[] = { {LV_PALETTE_BLUE, 0, LV_PALETTE_RED}, @@ -31,9 +32,9 @@ void verify_version() GLOBAL_CONFIG config = {0}; preferences.getBytes("global_config", &config, sizeof(config)); - Serial.printf("Config version: %d\n", config.version); + LOG_F(("Config version: %d\n", config.version)) if (config.version != CONFIG_VERSION) { - Serial.println("Clearing Global Config"); + LOG_LN("Clearing Global Config"); preferences.clear(); } @@ -131,4 +132,11 @@ void load_global_config() preferences.begin("global_config", true); preferences.getBytes("global_config", &global_config, sizeof(global_config)); preferences.end(); + + #if defined REPO_DEVELOPMENT && REPO_DEVELOPMENT == 1 + temporary_config.debug = true; + #else + temporary_config.debug = false; + #endif + temporary_config.remote_echo = true; } \ No newline at end of file diff --git a/CYD-Klipper/src/conf/global_config.h b/CYD-Klipper/src/conf/global_config.h index 31c16c8..334d1ee 100644 --- a/CYD-Klipper/src/conf/global_config.h +++ b/CYD-Klipper/src/conf/global_config.h @@ -5,6 +5,7 @@ #define CONFIG_VERSION 6 #define PRINTER_CONFIG_COUNT 8 +#define DISPLAY_SECRETS 0 enum { REMAINING_TIME_CALC_PERCENTAGE = 0, @@ -86,7 +87,14 @@ typedef struct _GLOBAL_CONFIG { unsigned char screen_timeout; unsigned char printer_index; } GLOBAL_CONFIG; + +// Volatile/temporary config that doesn't survive a reset +typedef struct _TEMPORARY_CONFIG { + bool debug : 1; + bool remote_echo : 1; +} TEMPORARY_CONFIG; + typedef struct _COLOR_DEF { lv_palette_t primary_color; short primary_color_light; @@ -94,8 +102,13 @@ typedef struct _COLOR_DEF { } COLOR_DEF; extern GLOBAL_CONFIG global_config; +extern TEMPORARY_CONFIG temporary_config; extern COLOR_DEF color_defs[]; +#define LOG(x) if(temporary_config.debug){ Serial.print(x);} +#define LOG_LN(x) if(temporary_config.debug){ Serial.println(x);} +#define LOG_F(x) if(temporary_config.debug){ Serial.printf x ;} // use with double braces, LOF_F(("x=%d\n",x)); + void write_global_config(); void verify_version(); void load_global_config(); diff --git a/CYD-Klipper/src/core/data_setup.cpp b/CYD-Klipper/src/core/data_setup.cpp index 584a6d6..159e57a 100644 --- a/CYD-Klipper/src/core/data_setup.cpp +++ b/CYD-Klipper/src/core/data_setup.cpp @@ -43,7 +43,7 @@ void unfreeze_render_thread(){ void send_gcode(bool wait, const char *gcode) { - Serial.printf("Sending gcode: %s\n", gcode); + LOG_F(("Sending gcode: %s\n", gcode)) SETUP_HTTP_CLIENT_FULL("/printer/gcode/script?script=" + urlEncode(gcode), false, wait ? 5000 : 750); try @@ -52,7 +52,7 @@ void send_gcode(bool wait, const char *gcode) } catch (...) { - Serial.println("Failed to send gcode"); + LOG_LN("Failed to send gcode"); } } @@ -73,7 +73,7 @@ int get_slicer_time_estimate_s() JsonDocument doc; deserializeJson(doc, client.getStream()); int time_estimate_s = doc["result"]["estimated_time"]; - Serial.printf("Got slicer time estimate: %ds\n", time_estimate_s); + LOG_F(("Got slicer time estimate: %ds\n", time_estimate_s)) return time_estimate_s; } @@ -330,7 +330,7 @@ void fetch_printer_data() unfreeze_render_thread(); } - Serial.printf("Failed to fetch printer data: %d\n", httpCode); + LOG_F(("Failed to fetch printer data: %d\n", httpCode)) } } diff --git a/CYD-Klipper/src/core/files_query.cpp b/CYD-Klipper/src/core/files_query.cpp index 204124b..e154ea9 100644 --- a/CYD-Klipper/src/core/files_query.cpp +++ b/CYD-Klipper/src/core/files_query.cpp @@ -29,7 +29,7 @@ FILESYSTEM_FILE* get_files(int limit) freeze_request_thread(); clear_files(); - Serial.printf("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()); + LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size())) std::list files; auto timer_request = millis(); @@ -41,7 +41,7 @@ FILESYSTEM_FILE* get_files(int limit) if (httpCode == 200){ JsonDocument doc; auto parseResult = deserializeJson(doc, client.getStream()); - Serial.printf("Json parse: %s\n", parseResult.c_str()); + LOG_F(("Json parse: %s\n", parseResult.c_str())) auto result = doc["result"].as(); for (auto file : result){ @@ -62,7 +62,7 @@ FILESYSTEM_FILE* get_files(int limit) f.name = (char*)malloc(strlen(path) + 1); if (f.name == NULL){ - Serial.println("Failed to allocate memory"); + LOG_LN("Failed to allocate memory"); continue; } strcpy(f.name, path); @@ -88,7 +88,7 @@ FILESYSTEM_FILE* get_files(int limit) FILESYSTEM_FILE* result = (FILESYSTEM_FILE*)malloc(size); if (result == NULL){ - Serial.println("Failed to allocate memory"); + LOG_LN("Failed to allocate memory"); for (auto file : files){ free(file.name); @@ -106,8 +106,8 @@ FILESYSTEM_FILE* get_files(int limit) result += 1; } - Serial.printf("Heap space post-file-parse: %d bytes\n", esp_get_free_heap_size()); - Serial.printf("Got %d files. Request took %dms, parsing took %dms\n", files.size(), timer_parse - timer_request, millis() - timer_parse); + LOG_F(("Heap space post-file-parse: %d bytes\n", esp_get_free_heap_size())) + LOG_F(("Got %d files. Request took %dms, parsing took %dms\n", files.size(), timer_parse - timer_request, millis() - timer_parse)) unfreeze_request_thread(); return last_query; } \ No newline at end of file diff --git a/CYD-Klipper/src/core/lv_setup.cpp b/CYD-Klipper/src/core/lv_setup.cpp index 264551d..b3c7d55 100644 --- a/CYD-Klipper/src/core/lv_setup.cpp +++ b/CYD-Klipper/src/core/lv_setup.cpp @@ -4,6 +4,7 @@ #include "lvgl.h" #include "../ui/ui_utils.h" #include +#include "../ui/serial/serial_console.h" #ifndef CPU_FREQ_HIGH #define CPU_FREQ_HIGH 240 @@ -117,6 +118,7 @@ void lv_do_calibration(){ while (true){ lv_handler(); + serial_console::run(); if (point[0] != 0 && point[1] != 0){ break; @@ -175,7 +177,7 @@ void lv_do_calibration(){ global_config.screen_cal_y_offset = 10.0 - ((float)y1 * global_config.screen_cal_y_mult); if (global_config.screen_cal_x_mult == std::numeric_limits::infinity() || global_config.screen_cal_y_mult == std::numeric_limits::infinity()){ - Serial.println("Calibration failed, please try again"); + LOG_LN("Calibration failed, please try again"); ESP.restart(); } @@ -183,7 +185,7 @@ void lv_do_calibration(){ write_global_config(); lv_obj_clean(lv_scr_act()); - Serial.printf("Calibration done: X*%.2f + %.2f, Y*%.2f + %.2f\n", global_config.screen_cal_x_mult, global_config.screen_cal_x_offset, global_config.screen_cal_y_mult, global_config.screen_cal_y_offset); + LOG_F(("Calibration done: X*%.2f + %.2f, Y*%.2f + %.2f\n", global_config.screen_cal_x_mult, global_config.screen_cal_x_offset, global_config.screen_cal_y_mult, global_config.screen_cal_y_offset)) } void set_screen_brightness() @@ -208,7 +210,7 @@ void screen_timer_wake() // Reset cpu freq setCpuFrequencyMhz(CPU_FREQ_HIGH); - Serial.printf("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()); + LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz())) #endif } @@ -220,7 +222,7 @@ void screen_timer_sleep(lv_timer_t *timer) // Screen is off, no need to make the cpu run fast, the user won't notice ;) setCpuFrequencyMhz(CPU_FREQ_LOW); - Serial.printf("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()); + LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz())) #endif } diff --git a/CYD-Klipper/src/lib/ESP32OTAPull.h b/CYD-Klipper/src/lib/ESP32OTAPull.h index f6ca39a..274ef21 100644 --- a/CYD-Klipper/src/lib/ESP32OTAPull.h +++ b/CYD-Klipper/src/lib/ESP32OTAPull.h @@ -100,7 +100,7 @@ class ESP32OTAPull size_t bytes_written = Update.write(buff, bytes_read); if (bytes_read != bytes_written) { - // Serial.printf("Unexpected error in OTA: %d %d %d\n", bytes_to_read, bytes_read, bytes_written); + // LOG_F(("Unexpected error in OTA: %d %d %d\n", bytes_to_read, bytes_read, bytes_written)) break; } offset += bytes_written; @@ -212,8 +212,8 @@ class ESP32OTAPull String CDevice = config["Device"].isNull() ? "" : (const char *)config["Device"]; CVersion = config["Version"].isNull() ? "" : (const char *)config["Version"]; String CConfig = config["Config"].isNull() ? "" : (const char *)config["Config"]; - //Serial.printf("Checking %s %s %s %s\n", CBoard.c_str(), CDevice.c_str(), CVersion.c_str(), CConfig.c_str()); - //Serial.printf("Against %s %s %s %s\n", BoardName.c_str(), DeviceName.c_str(), CurrentVersion, ConfigName.c_str()); + //LOG_F(("Checking %s %s %s %s\n", CBoard.c_str(), CDevice.c_str(), CVersion.c_str(), CConfig.c_str())) + //LOG_F(("Against %s %s %s %s\n", BoardName.c_str(), DeviceName.c_str(), CurrentVersion, ConfigName.c_str())) if ((CBoard.isEmpty() || CBoard == BoardName) && (CDevice.isEmpty() || CDevice == DeviceName) && (CConfig.isEmpty() || CConfig == ConfigName)) diff --git a/CYD-Klipper/src/main.cpp b/CYD-Klipper/src/main.cpp index 03e60ce..a170329 100644 --- a/CYD-Klipper/src/main.cpp +++ b/CYD-Klipper/src/main.cpp @@ -2,6 +2,7 @@ #include "core/screen_driver.h" #include "ui/wifi_setup.h" #include "ui/ip_setup.h" +#include "ui/serial/serial_console.h" #include "lvgl.h" #include "core/data_setup.h" #include "ui/main_ui.h" @@ -12,11 +13,11 @@ void setup() { Serial.begin(115200); - Serial.println("Hello World"); + serial_console::greet(); load_global_config(); screen_setup(); lv_setup(); - Serial.println("Screen init done"); + LOG_LN("Screen init done"); wifi_init(); ota_init(); @@ -31,6 +32,7 @@ void loop(){ wifi_ok(); data_loop(); lv_handler(); + serial_console::run(); if (is_ready_for_ota_update()) { diff --git a/CYD-Klipper/src/ui/gcode_img.cpp b/CYD-Klipper/src/ui/gcode_img.cpp index 8f20215..bae3057 100644 --- a/CYD-Klipper/src/ui/gcode_img.cpp +++ b/CYD-Klipper/src/ui/gcode_img.cpp @@ -14,7 +14,7 @@ static lv_img_dsc_t img_header = {0}; bool has_32_32_gcode_img(const char* filename) { if (filename == NULL){ - Serial.println("No gcode filename"); + LOG_LN("No gcode filename"); return false; } @@ -24,7 +24,7 @@ bool has_32_32_gcode_img(const char* filename) httpCode = client.GET(); } catch (...){ - Serial.println("Exception while fetching gcode img location"); + LOG_LN("Exception while fetching gcode img location"); return false; } @@ -52,14 +52,14 @@ bool has_32_32_gcode_img(const char* filename) } if (chosen_thumb != NULL){ - Serial.printf("Found 32x32 PNG gcode img at %s\n", filename); + LOG_F(("Found 32x32 PNG gcode img at %s\n", filename)) strcpy(img_filename_path, chosen_thumb); return true; } } else { - Serial.printf("Failed to fetch gcode image data: %d\n", httpCode); + LOG_F(("Failed to fetch gcode image data: %d\n", httpCode)) } return false; @@ -70,7 +70,7 @@ lv_obj_t* draw_gcode_img() clear_img_mem(); if (img_filename_path[0] == 0){ - Serial.println("No gcode img path"); + LOG_LN("No gcode img path"); return NULL; } @@ -81,7 +81,7 @@ lv_obj_t* draw_gcode_img() httpCode = client.GET(); } catch (...){ - Serial.println("Exception while fetching gcode img"); + LOG_LN("Exception while fetching gcode img"); return NULL; } @@ -90,13 +90,13 @@ lv_obj_t* draw_gcode_img() size_t len = client.getSize(); if (len <= 0) { - Serial.println("No gcode img data"); + LOG_LN("No gcode img data"); return NULL; } data_png = (unsigned char*)malloc(len + 1); if (len != client.getStream().readBytes(data_png, len)){ - Serial.println("Failed to read gcode img data"); + LOG_LN("Failed to read gcode img data"); clear_img_mem(); return NULL; } @@ -120,12 +120,12 @@ lv_obj_t* draw_gcode_img() lv_obj_t* show_gcode_img(const char* filename) { if (filename == NULL){ - Serial.println("No gcode filename"); + LOG_LN("No gcode filename"); return NULL; } if (!has_32_32_gcode_img(filename)){ - Serial.println("No 32x32 gcode img found"); + LOG_LN("No 32x32 gcode img found"); return NULL; } diff --git a/CYD-Klipper/src/ui/ip_setup.cpp b/CYD-Klipper/src/ui/ip_setup.cpp index 014734e..6070e92 100644 --- a/CYD-Klipper/src/ui/ip_setup.cpp +++ b/CYD-Klipper/src/ui/ip_setup.cpp @@ -10,6 +10,7 @@ #include "switch_printer.h" #include "macros.h" #include "../core/lv_setup.h" +#include "serial/serial_console.h" lv_obj_t * hostEntry; lv_obj_t * portEntry; @@ -66,7 +67,7 @@ connection_status_t verify_ip(){ return httpCode == 200 ? CONNECT_OK : CONNECT_FAIL; } catch (...) { - Serial.println("Failed to connect"); + LOG_LN("Failed to connect"); return CONNECT_FAIL; } } @@ -253,5 +254,6 @@ void ip_init(){ while (!get_current_printer_config()->ip_configured) { lv_handler(); + serial_console::run(); } } \ No newline at end of file diff --git a/CYD-Klipper/src/ui/macros.cpp b/CYD-Klipper/src/ui/macros.cpp index 4d70de2..9338e8a 100644 --- a/CYD-Klipper/src/ui/macros.cpp +++ b/CYD-Klipper/src/ui/macros.cpp @@ -8,7 +8,7 @@ PRINTER_CONFIG * curernt_config = NULL; static void btn_press(lv_event_t * e){ lv_obj_t * btn = lv_event_get_target(e); const char* macro = (const char*)lv_event_get_user_data(e); - Serial.printf("Macro: %s\n", macro); + LOG_F(("Macro: %s\n", macro)) send_gcode(false, macro); } @@ -25,7 +25,7 @@ static void power_device_toggle(lv_event_t * e) auto state = lv_obj_get_state(lv_event_get_target(e)); bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED); const char* power_device_name = (const char*)lv_event_get_user_data(e); - Serial.printf("Power Device: %s, State: %d -> %d\n", power_device_name, !checked, checked); + LOG_F(("Power Device: %s, State: %d -> %d\n", power_device_name, !checked, checked)) if (curernt_config != NULL) set_power_state(power_device_name, checked, curernt_config); diff --git a/CYD-Klipper/src/ui/ota_setup.cpp b/CYD-Klipper/src/ui/ota_setup.cpp index c381002..1501f52 100644 --- a/CYD-Klipper/src/ui/ota_setup.cpp +++ b/CYD-Klipper/src/ui/ota_setup.cpp @@ -49,7 +49,7 @@ void do_update_callback(int offset, int totallength) void ota_do_update(bool variant_automatic) { - Serial.println("Starting OTA Update"); + LOG_LN("Starting OTA Update"); lv_obj_clean(lv_scr_act()); lv_obj_t *panel = lv_create_empty_panel(lv_scr_act()); @@ -67,7 +67,7 @@ void ota_do_update(bool variant_automatic) lv_label_set_text(update_label, "0/0"); if (!variant_automatic) { - Serial.println("Freezing Background Tasks"); + LOG_LN("Freezing Background Tasks"); screen_timer_wake(); screen_timer_stop(); freeze_request_thread(); @@ -90,7 +90,7 @@ void ota_init() { //ota_pull.AllowDowngrades(true); int result = ota_pull.CheckForOTAUpdate(ota_url, REPO_VERSION, ESP32OTAPull::ActionType::DONT_DO_UPDATE); - Serial.printf("OTA Update Result: %d\n", result); + LOG_F(("OTA Update Result: %d\n", result)) update_available = result == ESP32OTAPull::UPDATE_AVAILABLE; if (global_config.auto_ota_update && update_available) diff --git a/CYD-Klipper/src/ui/panels/files_panel.cpp b/CYD-Klipper/src/ui/panels/files_panel.cpp index 0ccbf23..986aab6 100644 --- a/CYD-Klipper/src/ui/panels/files_panel.cpp +++ b/CYD-Klipper/src/ui/panels/files_panel.cpp @@ -19,7 +19,7 @@ static void btn_print_file(lv_event_t * e){ SETUP_HTTP_CLIENT("/printer/print/start?filename=" + urlEncode(selected_file->name)); int httpCode = client.POST(""); - Serial.printf("Print start: HTTP %d\n", httpCode); + LOG_F(("Print start: HTTP %d\n", httpCode)) } static void btn_print_file_verify(lv_event_t * e){ diff --git a/CYD-Klipper/src/ui/panels/move_panel.cpp b/CYD-Klipper/src/ui/panels/move_panel.cpp index 059bbfd..0c24c05 100644 --- a/CYD-Klipper/src/ui/panels/move_panel.cpp +++ b/CYD-Klipper/src/ui/panels/move_panel.cpp @@ -75,7 +75,7 @@ static void keyboard_cb_edit_move_increment(lv_event_t * e) } unsigned short* items[] = {get_current_printer_config()->printer_move_x_steps, get_current_printer_config()->printer_move_y_steps, get_current_printer_config()->printer_move_z_steps}; - Serial.printf("Setting increment %d %d %f\n", selected_column, selected_row, increment); + LOG_F(("Setting increment %d %d %f\n", selected_column, selected_row, increment)) items[selected_column][selected_row] = increment * 10; write_global_config(); nav_buttons_setup(PANEL_MOVE); diff --git a/CYD-Klipper/src/ui/serial/serial_commands.cpp b/CYD-Klipper/src/ui/serial/serial_commands.cpp new file mode 100644 index 0000000..f72a306 --- /dev/null +++ b/CYD-Klipper/src/ui/serial/serial_commands.cpp @@ -0,0 +1,370 @@ +#include "serial_commands.h" +#include +#include +#include +#include "../../conf/global_config.h" +#include "../switch_printer.h" + +namespace serial_console { + +/* How to add a new command: + - add the handler; function signature must be like: void handler(String argv[]) + - add {"command_name", &handler_name, argc} to commandHandlers + (argc = num of args + 1; argv[0] is always the command name) + - add the handler signature to serial_command.h + - add description to help() + - optionally add handling the new preference to sets() and settings() if it modifies global_config +*/ + +HANDLER commandHandlers[] = { + {"help", &help, 1}, + {"reset", &reset, 1}, + {"settings", &settings, 1}, + {"sets", &sets, 1}, + {"erase", &erase, 2}, + {"key", &key, 2}, + {"touch", &touch, 5}, + {"ssid", &ssid, 3}, + {"ip", &ip, 3}, + {"rotation", &rotation, 2}, + {"brightness", &brightness, 2}, + {"printer", &printer, 2}, + {"debug", &debug, 2}, + {"echo", &echo, 2} +}; + +void help(String argv[]) +{ + Serial.println("Serial console commands:"); + Serial.println(""); + Serial.println("settings - show current settings"); + Serial.println("sets - show current settings as commands for copy-paste"); + Serial.println("erase [item] - unconfigure parameter (key|touch|ssid|ip|all)"); + Serial.println("reset - restart CYD-klipper"); + Serial.println("touch [xm xo ym yo] - set touchscreen multipliers and offsets"); + Serial.println("ssid [name pass] - set the network SSID and password to connect to"); + Serial.println("ip [address port] - set Moonraker address"); + Serial.println("key [key] - set the Moonraker API key"); + Serial.println("rotation [on|off] - set rotate screen 180 degrees"); + Serial.println("brightness [num] - set screen brightness"); + Serial.println("printer [num|-1] - set active printer#; -1 for multi-printer mode off"); + Serial.println("debug [on|off] - set printing of debug messages to serial console (not saved)"); + Serial.println("echo [on|off] - set remote echo (eecchhoo ooffff) (not saved)"); + Serial.println("help - this help"); + Serial.println(""); + Serial.println("Settings are saved immediately but come into effect after reset"); + Serial.println("Unlike GUI, serial console does not validate if settings"); + Serial.println("you enter work correctly. This is a double-edged sword."); +} + +// this must be here, because serial_console doesn't have a clue about sizeof(commandHandlers) at compile time +int find_command(String cmd) +{ + for(int i=0; i < sizeof(commandHandlers) / sizeof(HANDLER); ++i) + { + if(cmd == commandHandlers[i].name) return i; + } + Serial.println("Unknown command"); + return -1; +} + + +void reset(String argv[]) +{ + ESP.restart(); +} + +void sets(String argv[]) +{ + + Serial.printf("printer %d\n", global_config.multi_printer_mode?global_config.printer_index:-1); + + if(global_config.wifi_configured) + { + Serial.printf("ssid %s %s\n",global_config.wifi_SSID, +#if DISPLAY_SECRETS + global_config.wifi_password +#else + "[redacted]" +#endif + ); + } + else + { + Serial.printf("erase ssid\n"); + } + + if(get_current_printer_config()->ip_configured) + { + Serial.printf("ip %s %d\n",get_current_printer_config()->klipper_host, get_current_printer_config()->klipper_port); + } + else + { + Serial.printf("erase ip\n"); + } + + if(get_current_printer_config()->auth_configured) + { + Serial.printf("key %s\n", +#if DISPLAY_SECRETS + get_current_printer_config()->klipper_auth +#else + "[redacted]" +#endif + ); + } + else + { + Serial.printf("erase key\n"); + } + + if(global_config.screen_calibrated) + { + Serial.printf("touch %f %f %f %f\n", + global_config.screen_cal_x_mult, global_config.screen_cal_x_offset, global_config.screen_cal_y_mult, global_config.screen_cal_y_offset); + } + else + { + Serial.printf("erase touch\n"); + } + + Serial.printf("rotation %s\n",global_config.rotate_screen?"on":"off"); + Serial.printf("brightness %d\n",global_config.brightness); +} + +void settings(String argv[]) +{ + + if(get_current_printer_config()->printer_name[0] != 0) + { + Serial.printf("Current printer# %d name: %s",global_config.printer_index, get_current_printer_config()->printer_name); + } + else + { + Serial.printf("Current printer# %d",global_config.printer_index); + } + Serial.printf(" Multi-printer mode %s\n",global_config.multi_printer_mode?"enabled":"disabled"); + + + if(global_config.wifi_configured) + { + Serial.printf("SSID: %s Password: %s\n",global_config.wifi_SSID, +#if DISPLAY_SECRETS + global_config.wifi_password +#else + "[redacted]" +#endif + + ); + } + else + { + Serial.printf("Wifi not configured\n"); + } + + if(get_current_printer_config()->ip_configured) + { + Serial.printf("Moonraker address: %s:%d\n",get_current_printer_config()->klipper_host, get_current_printer_config()->klipper_port); + } + else + { + Serial.printf("Moonraker address not configured\n"); + } + + if(get_current_printer_config()->auth_configured) + { + Serial.printf("Moonraker API key: %s\n", +#if DISPLAY_SECRETS + get_current_printer_config()->klipper_auth +#else + "[redacted]" +#endif + ); + } + else + { + Serial.printf("Moonraker API key not configured\n"); + } + + if(global_config.screen_calibrated) + { + Serial.printf("Screen coefficients: x_screen = %f * x_touch + %f; y_screen = %f * y_touch + %f\n", + global_config.screen_cal_x_mult, global_config.screen_cal_x_offset, global_config.screen_cal_y_mult, global_config.screen_cal_y_offset); + } + else + { + Serial.printf("Screen not calibrated\n"); + } + + Serial.printf("Screen orientation: %s\n",global_config.rotate_screen?"rotated":"normal"); + Serial.printf("Screen brightness: %d\n",global_config.brightness); +} + + +void erase_one(const String arg) +{ + if(arg == "key") + { + get_current_printer_config()->auth_configured = false; + // overwrite the key to make it unrecoverable for 3rd parties + memset(get_current_printer_config()->klipper_auth,0,32); + write_global_config(); + } + else if(arg == "ip") + { + get_current_printer_config()->ip_configured = false; + write_global_config(); + } + else if(arg == "touch") + { + global_config.screen_calibrated = false; + write_global_config(); + } + else if(arg == "ssid") + { + global_config.wifi_configured = false; + // overwrite the pass to make it unrecoverable for 3rd parties + memset(global_config.wifi_password,0,64); + write_global_config(); + } + else + { + Serial.println("Unknown key"); + } +} + +void erase(String argv[]) +{ + const String& arg=argv[1]; + if(arg != "all") + { + erase_one(arg); + } + else + { + erase_one("key"); + erase_one("ip"); + erase_one("touch"); + erase_one("ssid"); + } +} + +void key(String argv[]) +{ + if (argv[1].length() != 32) + { + Serial.println("Key must be 32 characters"); + return; + } + + get_current_printer_config()->auth_configured = true; + strncpy(get_current_printer_config()->klipper_auth, argv[1].c_str(), sizeof(global_config.printer_config[0].klipper_auth)); + write_global_config(); +} + +void touch(String argv[]) +{ + global_config.screen_cal_x_mult = argv[1].toFloat(); + global_config.screen_cal_x_offset = argv[2].toFloat(); + global_config.screen_cal_y_mult = argv[3].toFloat(); + global_config.screen_cal_y_offset = argv[4].toFloat(); + global_config.screen_calibrated = true; + write_global_config(); +} + +void ssid(String argv[]) +{ + strncpy(global_config.wifi_SSID, argv[1].c_str(), sizeof(global_config.wifi_SSID)-1); + strncpy(global_config.wifi_password, argv[2].c_str(), sizeof(global_config.wifi_password)-1); + global_config.wifi_configured = true; + write_global_config(); +} + +void ip(String argv[]) +{ + strncpy(get_current_printer_config()->klipper_host, argv[1].c_str(), sizeof(global_config.printer_config[0].klipper_host)-1); + get_current_printer_config()->klipper_port = argv[2].toInt(); + get_current_printer_config()->ip_configured = true; + write_global_config(); +} + +void rotation(String argv[]) +{ + if(argv[1] == "on") + { + global_config.rotate_screen = true; + write_global_config(); + } + else if (argv[1] == "off") + { + global_config.rotate_screen = false; + write_global_config(); + } + else + { + Serial.println("Rotation can be on or off"); + } +} + +void brightness(String argv[]) +{ + global_config.brightness = argv[1].toInt(); + write_global_config(); +} + + +void printer(String argv[]) +{ + int ndx = argv[1].toInt(); + if(ndx == -1) + { + global_config.multi_printer_mode = false; + switch_printer(0); + } + else if( ndx >=0 && ndx < PRINTER_CONFIG_COUNT) + { + global_config.multi_printer_mode = true; + switch_printer(ndx); + } + else + { + Serial.println("Printer index out of range"); + } + +} + +void debug(String argv[]) +{ + if(argv[1] == "on") + { + temporary_config.debug = true; + + } + else if (argv[1] == "off") + { + temporary_config.debug = false; + } + else + { + Serial.println("debug can be on or off"); + } +} + +void echo(String argv[]) +{ + if(argv[1] == "on") + { + temporary_config.remote_echo = true; + } + else if (argv[1] == "off") + { + temporary_config.remote_echo = false; + } + else + { + Serial.println("Echo can be on or off"); + } +} + + +} \ No newline at end of file diff --git a/CYD-Klipper/src/ui/serial/serial_commands.h b/CYD-Klipper/src/ui/serial/serial_commands.h new file mode 100644 index 0000000..22550ee --- /dev/null +++ b/CYD-Klipper/src/ui/serial/serial_commands.h @@ -0,0 +1,29 @@ +#include + +namespace serial_console { + +typedef struct _HANDLER { + const char* name; + void (* function)(String argv[]); + int argc; +} HANDLER; + +extern HANDLER commandHandlers[]; + +void help(String argv[]); +void reset(String argv[]); +void settings(String argv[]); +void sets(String argv[]); +void erase(String argv[]); +void key(String argv[]); +void touch(String argv[]); +void ssid(String argv[]); +void ip(String argv[]); +void rotation(String argv[]); +void brightness(String argv[]); +void printer(String argv[]); +void debug(String argv[]); +void echo(String argv[]); + +int find_command(String cmd); +} \ No newline at end of file diff --git a/CYD-Klipper/src/ui/serial/serial_console.cpp b/CYD-Klipper/src/ui/serial/serial_console.cpp new file mode 100644 index 0000000..e688147 --- /dev/null +++ b/CYD-Klipper/src/ui/serial/serial_console.cpp @@ -0,0 +1,153 @@ +#include "serial_commands.h" +#include "../../conf/global_config.h" +#include +#include + +#define MAX_COMDLINE_SIZE 80 +#define MAX_WORDS 6 + +namespace serial_console +{ + + /* + * read_string_until: Non-blocking replacement for Serial.readStringUntil().. + * With delimeter '\n' acts as 'read line'. + * If input (line) size exceeds provided buffer's size, that entire input (until next delimeter) is silently discarded. + * Serial.available() can return true without \n being ever in the buffer, so we don't trust it here. + */ + + bool read_string_until(char delimiter, char *result, int max_len) + { + static int index = 0; + int c; // read character, -1 if none + int cnt = 100; // limit on amount of iterations in one go; we're supposed to be non-blocking! + + while ((c = Serial.read()) != -1 && cnt > 0) + { + --cnt; + + // backspace + if (c == 8) + { + if (index > 0) + { + if(temporary_config.remote_echo) Serial.print("\x08 \x08"); // overwrite last character with space and move cursor 1 back. + index--; + } + continue; + } + + if(temporary_config.remote_echo) Serial.print((char)c); // echo + + // Buffer overflow handling: + // start treating current buffer as invalid: + // - stop collecting more data + // - return false on delimeter, flushing everything collected, + // - restart collection from scratch after delimeter, + // - return control as normal. + + if (index >= max_len - 1) + { + if (c == delimiter) // got delimeter: flush buffer quietly, restart collection. + { + index = 0; + return false; + } + else + continue; // discard any data past the end of the buffer, keep reading + } + + result[index++] = c; + + // delimiter was found + if (c == delimiter) + { + result[index] = '\0'; // Null-terminate the string + index = 0; + return true; // Success: Delimiter found + } + } + + return false; // still waiting for delimeter + } + + // split input into words. + // assumes input ends with '\n', also ends parsing if found MAX_WORDS already. + int tokenize(String results[], char *input) + { + int index = 0; + int word_count = 0; + String word = ""; + do + { + if (input[index] == '\t' || input[index] == ' ' || input[index] == '\n' || input[index] == '\r' || input[index] == '\0') + { + if (word.length() > 0) + { + results[word_count] = word; + ++word_count; + if (word_count >= MAX_WORDS) + { + return word_count; + } + word = ""; + } + + if (input[index] == '\n' || input[index] == '\0') + { + return word_count; + } + index++; + } + else + { + word += input[index]; + index++; + } + } while (1); + } + + void greet() + { + Serial.println("CYD-Klipper " REPO_VERSION); + Serial.println("Type 'help' for serial console command list"); + Serial.print("> "); + } + + bool verify_arg_count(int got, int expected) + { + if (got != expected) + { + Serial.printf("Command expects %d argument%s, %d given.\n", expected - 1, expected == 2 ? "" : "s", got - 1); + return false; + } + return true; + } + + // main "engine": non-blockingly read until newline found, parse, execute. + void run() + { + static char cmdline[MAX_COMDLINE_SIZE + 1] = {0}; + if (!read_string_until('\n', cmdline, MAX_COMDLINE_SIZE)) + return; + + String argv[MAX_WORDS]; + int argc = tokenize(argv, cmdline); + + if (argc > 0) + { + do + { + int cmd_id = find_command(argv[0]); + if (cmd_id == -1) + break; + if (!verify_arg_count(argc, commandHandlers[cmd_id].argc)) + break; + commandHandlers[cmd_id].function(argv); + } while (0); + } + + Serial.print("> "); + } + +} \ No newline at end of file diff --git a/CYD-Klipper/src/ui/serial/serial_console.h b/CYD-Klipper/src/ui/serial/serial_console.h new file mode 100644 index 0000000..31cd10c --- /dev/null +++ b/CYD-Klipper/src/ui/serial/serial_console.h @@ -0,0 +1,8 @@ + + +namespace serial_console { + +void greet(); +void run(); + +} \ No newline at end of file diff --git a/CYD-Klipper/src/ui/wifi_setup.cpp b/CYD-Klipper/src/ui/wifi_setup.cpp index 027011d..14d6c6e 100644 --- a/CYD-Klipper/src/ui/wifi_setup.cpp +++ b/CYD-Klipper/src/ui/wifi_setup.cpp @@ -5,6 +5,7 @@ #include "WiFi.h" #include "../core/data_setup.h" #include "../core/lv_setup.h" +#include "serial/serial_console.h" void wifi_init_inner(); void wifi_pass_entry(const char* ssid); @@ -87,7 +88,7 @@ void wifi_pass_entry(const char* ssid) static void wifi_btn_event_handler(lv_event_t * e){ delay(100); char* ssid = (char*)e->user_data; - Serial.println(ssid); + LOG_LN(ssid); wifi_pass_entry(ssid); } @@ -97,7 +98,7 @@ static void wifi_keyboard_cb_manual_ssid(lv_event_t * e){ const char * text = lv_textarea_get_text(ta); char * text_copy = (char*)malloc(strlen(text) + 1); strcpy(text_copy, text); - Serial.println(text_copy); + LOG_LN(text_copy); wifi_pass_entry(text_copy); } @@ -119,7 +120,7 @@ void wifi_init_inner(){ WiFi.begin(global_config.wifi_SSID, global_config.wifi_password); } - Serial.printf("Connecting to %s with a password length of %d\n", global_config.wifi_SSID, strlen(global_config.wifi_password)); + LOG_F(("Connecting to %s with a password length of %d\n", global_config.wifi_SSID, strlen(global_config.wifi_password))) lv_obj_t * label = lv_label_create(lv_scr_act()); lv_label_set_text(label, "Connecting to WiFi"); @@ -221,10 +222,12 @@ void wifi_init(){ while (!global_config.wifi_configured || WiFi.status() != WL_CONNECTED){ if (millis() - print_timer > print_freq){ print_timer = millis(); - Serial.printf("WiFi Status: %s\n", errs[WiFi.status()]); + LOG_F(("WiFi Status: %s\n", errs[WiFi.status()])) } lv_handler(); + serial_console::run(); + } } @@ -232,7 +235,7 @@ ulong start_time_recovery = 0; void wifi_ok(){ if (WiFi.status() != WL_CONNECTED){ - Serial.println("WiFi Connection Lost. Reconnecting..."); + LOG_LN("WiFi Connection Lost. Reconnecting..."); freeze_request_thread(); WiFi.disconnect(); delay(5000); // Wait for the WiFi to disconnect @@ -250,9 +253,9 @@ void wifi_ok(){ while (WiFi.status() != WL_CONNECTED){ delay(1000); - Serial.printf("WiFi Status: %s\n", errs[WiFi.status()]); + LOG_F(("WiFi Status: %s\n", errs[WiFi.status()])) if (millis() - start_time_recovery > 15000){ - Serial.println("WiFi Connection failed to reconnect. Restarting..."); + LOG_LN("WiFi Connection failed to reconnect. Restarting..."); ESP.restart(); } } diff --git a/README.md b/README.md index 36359ad..13e7171 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,22 @@ A ESP32-2432S028R is required to run this project. You can find out where to buy - Extrude/Retract filament - Execute predefined gcode macros - Toggle Moonraker power devices +- OTA updates +- Serial console over USB (115200 8n1, echo off, LF/LF) ### Install -[There is a web-based installer available. This is only supported on Chrome, Edge or Opera, and only on Desktop.](https://suchmememanyskill.github.io/CYD-Klipper/) +[There is a web-based installer available. This is only supported on Chrome, Edge, Arc or Opera, and only on Desktop.](https://suchmememanyskill.github.io/CYD-Klipper/) On initial install, all data should be wiped. On updates, data should be able to be kept without issues. -There are no 'over the air' updates. Each update has to be applied manually. +When there is an update available, a button in the settings will appear that can be pressed to update. If automatic updates are preferred, there is a toggle in the settings to automatically update. This will right after connecting to wifi update the screen. + +### Donate + +If you found this project helpful, please consider a donation [to my Ko-Fi](https://ko-fi.com/suchmememanyskill). It would help out a lot in the development of this project, due to the need to buy the screens. + +Thank you! ### Screenshots (Quite literally shots of the screen. I'm sorry) @@ -47,4 +55,4 @@ There are no 'over the air' updates. Each update has to be applied manually. - [xtouch](https://github.com/xperiments-in/xtouch) - [ESP32-Cheap-Yellow-Display](https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display) - [OperatorB](https://github.com/OperatorB) for the ESP32-3248S035C display driver -- [esp32-smartdisplay](https://github.com/rzeldent/esp32-smartdisplay) \ No newline at end of file +- [esp32-smartdisplay](https://github.com/rzeldent/esp32-smartdisplay) diff --git a/_site/index.html b/_site/index.html index 20a3822..cba34c8 100644 --- a/_site/index.html +++ b/_site/index.html @@ -8,6 +8,11 @@ font-family: 'Roboto', sans-serif; } + TT { + font-family: 'Terminal', monospace; + background-color: #080a0b; + } + body { background-color: #181a1b; color: white; @@ -27,7 +32,7 @@ color: #F44; } - .install { + .configure { margin-bottom: 300px; } @@ -67,7 +72,7 @@

CYD-Klipper

An implementation of a Klipper status display on an ESP32 + screen.
Uses Moonraker to fetch data.
Source code is available on GitHub.

- +

Changelog

@@ -97,5 +102,13 @@

Install

+ +
+

Config

+

After installing CYD-Klipper, you can configure it using its touchscreen, or serial console. To use the serial console, + click 'Connect', select the serial port, select "LOGS & CONSOLE" and reset the board without holding BOOT.
+ Type help for list of available commands. You're interested in ssid, ip and key. (while you can set touch, it's really better done using the touchscreen.) + +

- \ No newline at end of file +