Serial console (#117)

* Update readme

* Update README.md (#95)

* v1.6.4 (#113)

* Fix gcode previews with special chars not loading

* Add .gitignore file (#108)

* Bulletproof ci.py (#107)

* Implement file sorting (implement #89)

* Set chip family to ESP32-S3 for specific models (fix #67)

* Add files menu to params panel while printing (implement #80)

* Update ci.py (#110)

Typo fix for ESP32-S3 boards array name

---------

Co-authored-by: Sebastian Göls <6608231+Abrynos@users.noreply.github.com>
Co-authored-by: Miroslav Zuzelka <mzuzelka@gmail.com>

* Added serial console

* backspace, minor tweaks

* - added temporary_config alongside global_config, to hold non-persistent configuration
- added macros LOG, LOG_F, LOG_LN conditional on temporary_config.debug for Serial.print/printf/println
- put all debug to console behind these macros
- added 'debug' serial command to toggle temporary_config.debug, defaults to REPO_DEVELOPMENT
- added 'echo' serial command to toggle remote echo, temporary_config.remote_echo
- added #define DISPLAY_SECRETS to global_config.h, to censor wifi password and api key on serial console
- added entries about serial console to README.md and to _site/index.html

* restored -DREPO_DEVELOPMENT=1 (m)

* Build failed on esp32-3248S035C, reduced console input buffer size (static char cmdline) as it was failing to fit.

* typo

* A lot of what should be LOG_F was LOG_LN instead
Handling undefined REPO_DEVELOPMENT when initializing temporary_config.debug

---------

Co-authored-by: Sims <38142618+suchmememanyskill@users.noreply.github.com>
Co-authored-by: Beebles <102569435+beebls@users.noreply.github.com>
Co-authored-by: Sebastian Göls <6608231+Abrynos@users.noreply.github.com>
Co-authored-by: Miroslav Zuzelka <mzuzelka@gmail.com>
Co-authored-by: Bartosz Wucke <b.wucke@a-ster.pl>
This commit is contained in:
bwucke
2024-08-02 21:16:45 +02:00
committed by GitHub
parent 19cfaefd36
commit 41aa073ae0
21 changed files with 664 additions and 53 deletions

View File

@@ -57,7 +57,7 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build 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: steps:
- name: Print GitHub event name - name: Print GitHub event name
run: | run: |

View File

@@ -3,6 +3,7 @@
#include "lvgl.h" #include "lvgl.h"
GLOBAL_CONFIG global_config = {0}; GLOBAL_CONFIG global_config = {0};
TEMPORARY_CONFIG temporary_config = {0};
COLOR_DEF color_defs[] = { COLOR_DEF color_defs[] = {
{LV_PALETTE_BLUE, 0, LV_PALETTE_RED}, {LV_PALETTE_BLUE, 0, LV_PALETTE_RED},
@@ -31,9 +32,9 @@ void verify_version()
GLOBAL_CONFIG config = {0}; GLOBAL_CONFIG config = {0};
preferences.getBytes("global_config", &config, sizeof(config)); 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) { if (config.version != CONFIG_VERSION) {
Serial.println("Clearing Global Config"); LOG_LN("Clearing Global Config");
preferences.clear(); preferences.clear();
} }
@@ -131,4 +132,11 @@ void load_global_config()
preferences.begin("global_config", true); preferences.begin("global_config", true);
preferences.getBytes("global_config", &global_config, sizeof(global_config)); preferences.getBytes("global_config", &global_config, sizeof(global_config));
preferences.end(); preferences.end();
#if defined REPO_DEVELOPMENT && REPO_DEVELOPMENT == 1
temporary_config.debug = true;
#else
temporary_config.debug = false;
#endif
temporary_config.remote_echo = true;
} }

View File

@@ -5,6 +5,7 @@
#define CONFIG_VERSION 6 #define CONFIG_VERSION 6
#define PRINTER_CONFIG_COUNT 8 #define PRINTER_CONFIG_COUNT 8
#define DISPLAY_SECRETS 0
enum { enum {
REMAINING_TIME_CALC_PERCENTAGE = 0, REMAINING_TIME_CALC_PERCENTAGE = 0,
@@ -86,7 +87,14 @@ typedef struct _GLOBAL_CONFIG {
unsigned char screen_timeout; unsigned char screen_timeout;
unsigned char printer_index; unsigned char printer_index;
} GLOBAL_CONFIG; } 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 { typedef struct _COLOR_DEF {
lv_palette_t primary_color; lv_palette_t primary_color;
short primary_color_light; short primary_color_light;
@@ -94,8 +102,13 @@ typedef struct _COLOR_DEF {
} COLOR_DEF; } COLOR_DEF;
extern GLOBAL_CONFIG global_config; extern GLOBAL_CONFIG global_config;
extern TEMPORARY_CONFIG temporary_config;
extern COLOR_DEF color_defs[]; 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 write_global_config();
void verify_version(); void verify_version();
void load_global_config(); void load_global_config();

View File

@@ -43,7 +43,7 @@ void unfreeze_render_thread(){
void send_gcode(bool wait, const char *gcode) 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); SETUP_HTTP_CLIENT_FULL("/printer/gcode/script?script=" + urlEncode(gcode), false, wait ? 5000 : 750);
try try
@@ -52,7 +52,7 @@ void send_gcode(bool wait, const char *gcode)
} }
catch (...) 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; JsonDocument doc;
deserializeJson(doc, client.getStream()); deserializeJson(doc, client.getStream());
int time_estimate_s = doc["result"]["estimated_time"]; 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; return time_estimate_s;
} }
@@ -330,7 +330,7 @@ void fetch_printer_data()
unfreeze_render_thread(); unfreeze_render_thread();
} }
Serial.printf("Failed to fetch printer data: %d\n", httpCode); LOG_F(("Failed to fetch printer data: %d\n", httpCode))
} }
} }

View File

@@ -29,7 +29,7 @@ FILESYSTEM_FILE* get_files(int limit)
freeze_request_thread(); freeze_request_thread();
clear_files(); 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<FILESYSTEM_FILE> files; std::list<FILESYSTEM_FILE> files;
auto timer_request = millis(); auto timer_request = millis();
@@ -41,7 +41,7 @@ FILESYSTEM_FILE* get_files(int limit)
if (httpCode == 200){ if (httpCode == 200){
JsonDocument doc; JsonDocument doc;
auto parseResult = deserializeJson(doc, client.getStream()); 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<JsonArray>(); auto result = doc["result"].as<JsonArray>();
for (auto file : result){ for (auto file : result){
@@ -62,7 +62,7 @@ FILESYSTEM_FILE* get_files(int limit)
f.name = (char*)malloc(strlen(path) + 1); f.name = (char*)malloc(strlen(path) + 1);
if (f.name == NULL){ if (f.name == NULL){
Serial.println("Failed to allocate memory"); LOG_LN("Failed to allocate memory");
continue; continue;
} }
strcpy(f.name, path); strcpy(f.name, path);
@@ -88,7 +88,7 @@ FILESYSTEM_FILE* get_files(int limit)
FILESYSTEM_FILE* result = (FILESYSTEM_FILE*)malloc(size); FILESYSTEM_FILE* result = (FILESYSTEM_FILE*)malloc(size);
if (result == NULL){ if (result == NULL){
Serial.println("Failed to allocate memory"); LOG_LN("Failed to allocate memory");
for (auto file : files){ for (auto file : files){
free(file.name); free(file.name);
@@ -106,8 +106,8 @@ FILESYSTEM_FILE* get_files(int limit)
result += 1; result += 1;
} }
Serial.printf("Heap space post-file-parse: %d bytes\n", esp_get_free_heap_size()); LOG_F(("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(("Got %d files. Request took %dms, parsing took %dms\n", files.size(), timer_parse - timer_request, millis() - timer_parse))
unfreeze_request_thread(); unfreeze_request_thread();
return last_query; return last_query;
} }

View File

@@ -4,6 +4,7 @@
#include "lvgl.h" #include "lvgl.h"
#include "../ui/ui_utils.h" #include "../ui/ui_utils.h"
#include <Esp.h> #include <Esp.h>
#include "../ui/serial/serial_console.h"
#ifndef CPU_FREQ_HIGH #ifndef CPU_FREQ_HIGH
#define CPU_FREQ_HIGH 240 #define CPU_FREQ_HIGH 240
@@ -117,6 +118,7 @@ void lv_do_calibration(){
while (true){ while (true){
lv_handler(); lv_handler();
serial_console::run();
if (point[0] != 0 && point[1] != 0){ if (point[0] != 0 && point[1] != 0){
break; 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); 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<float>::infinity() || global_config.screen_cal_y_mult == std::numeric_limits<float>::infinity()){ if (global_config.screen_cal_x_mult == std::numeric_limits<float>::infinity() || global_config.screen_cal_y_mult == std::numeric_limits<float>::infinity()){
Serial.println("Calibration failed, please try again"); LOG_LN("Calibration failed, please try again");
ESP.restart(); ESP.restart();
} }
@@ -183,7 +185,7 @@ void lv_do_calibration(){
write_global_config(); write_global_config();
lv_obj_clean(lv_scr_act()); 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() void set_screen_brightness()
@@ -208,7 +210,7 @@ void screen_timer_wake()
// Reset cpu freq // Reset cpu freq
setCpuFrequencyMhz(CPU_FREQ_HIGH); setCpuFrequencyMhz(CPU_FREQ_HIGH);
Serial.printf("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()); LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()))
#endif #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 ;) // Screen is off, no need to make the cpu run fast, the user won't notice ;)
setCpuFrequencyMhz(CPU_FREQ_LOW); setCpuFrequencyMhz(CPU_FREQ_LOW);
Serial.printf("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()); LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()))
#endif #endif
} }

View File

@@ -100,7 +100,7 @@ private:
size_t bytes_written = Update.write(buff, bytes_read); size_t bytes_written = Update.write(buff, bytes_read);
if (bytes_read != bytes_written) 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; break;
} }
offset += bytes_written; offset += bytes_written;
@@ -212,8 +212,8 @@ public:
String CDevice = config["Device"].isNull() ? "" : (const char *)config["Device"]; String CDevice = config["Device"].isNull() ? "" : (const char *)config["Device"];
CVersion = config["Version"].isNull() ? "" : (const char *)config["Version"]; CVersion = config["Version"].isNull() ? "" : (const char *)config["Version"];
String CConfig = config["Config"].isNull() ? "" : (const char *)config["Config"]; 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()); //LOG_F(("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(("Against %s %s %s %s\n", BoardName.c_str(), DeviceName.c_str(), CurrentVersion, ConfigName.c_str()))
if ((CBoard.isEmpty() || CBoard == BoardName) && if ((CBoard.isEmpty() || CBoard == BoardName) &&
(CDevice.isEmpty() || CDevice == DeviceName) && (CDevice.isEmpty() || CDevice == DeviceName) &&
(CConfig.isEmpty() || CConfig == ConfigName)) (CConfig.isEmpty() || CConfig == ConfigName))

View File

@@ -2,6 +2,7 @@
#include "core/screen_driver.h" #include "core/screen_driver.h"
#include "ui/wifi_setup.h" #include "ui/wifi_setup.h"
#include "ui/ip_setup.h" #include "ui/ip_setup.h"
#include "ui/serial/serial_console.h"
#include "lvgl.h" #include "lvgl.h"
#include "core/data_setup.h" #include "core/data_setup.h"
#include "ui/main_ui.h" #include "ui/main_ui.h"
@@ -12,11 +13,11 @@
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println("Hello World"); serial_console::greet();
load_global_config(); load_global_config();
screen_setup(); screen_setup();
lv_setup(); lv_setup();
Serial.println("Screen init done"); LOG_LN("Screen init done");
wifi_init(); wifi_init();
ota_init(); ota_init();
@@ -31,6 +32,7 @@ void loop(){
wifi_ok(); wifi_ok();
data_loop(); data_loop();
lv_handler(); lv_handler();
serial_console::run();
if (is_ready_for_ota_update()) if (is_ready_for_ota_update())
{ {

View File

@@ -14,7 +14,7 @@ static lv_img_dsc_t img_header = {0};
bool has_32_32_gcode_img(const char* filename) bool has_32_32_gcode_img(const char* filename)
{ {
if (filename == NULL){ if (filename == NULL){
Serial.println("No gcode filename"); LOG_LN("No gcode filename");
return false; return false;
} }
@@ -24,7 +24,7 @@ bool has_32_32_gcode_img(const char* filename)
httpCode = client.GET(); httpCode = client.GET();
} }
catch (...){ catch (...){
Serial.println("Exception while fetching gcode img location"); LOG_LN("Exception while fetching gcode img location");
return false; return false;
} }
@@ -52,14 +52,14 @@ bool has_32_32_gcode_img(const char* filename)
} }
if (chosen_thumb != NULL){ 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); strcpy(img_filename_path, chosen_thumb);
return true; return true;
} }
} }
else 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; return false;
@@ -70,7 +70,7 @@ lv_obj_t* draw_gcode_img()
clear_img_mem(); clear_img_mem();
if (img_filename_path[0] == 0){ if (img_filename_path[0] == 0){
Serial.println("No gcode img path"); LOG_LN("No gcode img path");
return NULL; return NULL;
} }
@@ -81,7 +81,7 @@ lv_obj_t* draw_gcode_img()
httpCode = client.GET(); httpCode = client.GET();
} }
catch (...){ catch (...){
Serial.println("Exception while fetching gcode img"); LOG_LN("Exception while fetching gcode img");
return NULL; return NULL;
} }
@@ -90,13 +90,13 @@ lv_obj_t* draw_gcode_img()
size_t len = client.getSize(); size_t len = client.getSize();
if (len <= 0) if (len <= 0)
{ {
Serial.println("No gcode img data"); LOG_LN("No gcode img data");
return NULL; return NULL;
} }
data_png = (unsigned char*)malloc(len + 1); data_png = (unsigned char*)malloc(len + 1);
if (len != client.getStream().readBytes(data_png, len)){ 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(); clear_img_mem();
return NULL; return NULL;
} }
@@ -120,12 +120,12 @@ lv_obj_t* draw_gcode_img()
lv_obj_t* show_gcode_img(const char* filename) lv_obj_t* show_gcode_img(const char* filename)
{ {
if (filename == NULL){ if (filename == NULL){
Serial.println("No gcode filename"); LOG_LN("No gcode filename");
return NULL; return NULL;
} }
if (!has_32_32_gcode_img(filename)){ if (!has_32_32_gcode_img(filename)){
Serial.println("No 32x32 gcode img found"); LOG_LN("No 32x32 gcode img found");
return NULL; return NULL;
} }

View File

@@ -10,6 +10,7 @@
#include "switch_printer.h" #include "switch_printer.h"
#include "macros.h" #include "macros.h"
#include "../core/lv_setup.h" #include "../core/lv_setup.h"
#include "serial/serial_console.h"
lv_obj_t * hostEntry; lv_obj_t * hostEntry;
lv_obj_t * portEntry; lv_obj_t * portEntry;
@@ -66,7 +67,7 @@ connection_status_t verify_ip(){
return httpCode == 200 ? CONNECT_OK : CONNECT_FAIL; return httpCode == 200 ? CONNECT_OK : CONNECT_FAIL;
} }
catch (...) { catch (...) {
Serial.println("Failed to connect"); LOG_LN("Failed to connect");
return CONNECT_FAIL; return CONNECT_FAIL;
} }
} }
@@ -253,5 +254,6 @@ void ip_init(){
while (!get_current_printer_config()->ip_configured) while (!get_current_printer_config()->ip_configured)
{ {
lv_handler(); lv_handler();
serial_console::run();
} }
} }

View File

@@ -8,7 +8,7 @@ PRINTER_CONFIG * curernt_config = NULL;
static void btn_press(lv_event_t * e){ static void btn_press(lv_event_t * e){
lv_obj_t * btn = lv_event_get_target(e); lv_obj_t * btn = lv_event_get_target(e);
const char* macro = (const char*)lv_event_get_user_data(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); 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)); auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED); bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
const char* power_device_name = (const char*)lv_event_get_user_data(e); 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) if (curernt_config != NULL)
set_power_state(power_device_name, checked, curernt_config); set_power_state(power_device_name, checked, curernt_config);

View File

@@ -49,7 +49,7 @@ void do_update_callback(int offset, int totallength)
void ota_do_update(bool variant_automatic) 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_clean(lv_scr_act());
lv_obj_t *panel = lv_create_empty_panel(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"); lv_label_set_text(update_label, "0/0");
if (!variant_automatic) { if (!variant_automatic) {
Serial.println("Freezing Background Tasks"); LOG_LN("Freezing Background Tasks");
screen_timer_wake(); screen_timer_wake();
screen_timer_stop(); screen_timer_stop();
freeze_request_thread(); freeze_request_thread();
@@ -90,7 +90,7 @@ void ota_init()
{ {
//ota_pull.AllowDowngrades(true); //ota_pull.AllowDowngrades(true);
int result = ota_pull.CheckForOTAUpdate(ota_url, REPO_VERSION, ESP32OTAPull::ActionType::DONT_DO_UPDATE); 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; update_available = result == ESP32OTAPull::UPDATE_AVAILABLE;
if (global_config.auto_ota_update && update_available) if (global_config.auto_ota_update && update_available)

View File

@@ -19,7 +19,7 @@ static void btn_print_file(lv_event_t * e){
SETUP_HTTP_CLIENT("/printer/print/start?filename=" + urlEncode(selected_file->name)); SETUP_HTTP_CLIENT("/printer/print/start?filename=" + urlEncode(selected_file->name));
int httpCode = client.POST(""); 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){ static void btn_print_file_verify(lv_event_t * e){

View File

@@ -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}; 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; items[selected_column][selected_row] = increment * 10;
write_global_config(); write_global_config();
nav_buttons_setup(PANEL_MOVE); nav_buttons_setup(PANEL_MOVE);

View File

@@ -0,0 +1,370 @@
#include "serial_commands.h"
#include <HardwareSerial.h>
#include <Esp.h>
#include <cstring>
#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");
}
}
}

View File

@@ -0,0 +1,29 @@
#include <WString.h>
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);
}

View File

@@ -0,0 +1,153 @@
#include "serial_commands.h"
#include "../../conf/global_config.h"
#include <HardwareSerial.h>
#include <WString.h>
#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("> ");
}
}

View File

@@ -0,0 +1,8 @@
namespace serial_console {
void greet();
void run();
}

View File

@@ -5,6 +5,7 @@
#include "WiFi.h" #include "WiFi.h"
#include "../core/data_setup.h" #include "../core/data_setup.h"
#include "../core/lv_setup.h" #include "../core/lv_setup.h"
#include "serial/serial_console.h"
void wifi_init_inner(); void wifi_init_inner();
void wifi_pass_entry(const char* ssid); 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){ static void wifi_btn_event_handler(lv_event_t * e){
delay(100); delay(100);
char* ssid = (char*)e->user_data; char* ssid = (char*)e->user_data;
Serial.println(ssid); LOG_LN(ssid);
wifi_pass_entry(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); const char * text = lv_textarea_get_text(ta);
char * text_copy = (char*)malloc(strlen(text) + 1); char * text_copy = (char*)malloc(strlen(text) + 1);
strcpy(text_copy, text); strcpy(text_copy, text);
Serial.println(text_copy); LOG_LN(text_copy);
wifi_pass_entry(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); 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_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Connecting to WiFi"); lv_label_set_text(label, "Connecting to WiFi");
@@ -221,10 +222,12 @@ void wifi_init(){
while (!global_config.wifi_configured || WiFi.status() != WL_CONNECTED){ while (!global_config.wifi_configured || WiFi.status() != WL_CONNECTED){
if (millis() - print_timer > print_freq){ if (millis() - print_timer > print_freq){
print_timer = millis(); print_timer = millis();
Serial.printf("WiFi Status: %s\n", errs[WiFi.status()]); LOG_F(("WiFi Status: %s\n", errs[WiFi.status()]))
} }
lv_handler(); lv_handler();
serial_console::run();
} }
} }
@@ -232,7 +235,7 @@ ulong start_time_recovery = 0;
void wifi_ok(){ void wifi_ok(){
if (WiFi.status() != WL_CONNECTED){ if (WiFi.status() != WL_CONNECTED){
Serial.println("WiFi Connection Lost. Reconnecting..."); LOG_LN("WiFi Connection Lost. Reconnecting...");
freeze_request_thread(); freeze_request_thread();
WiFi.disconnect(); WiFi.disconnect();
delay(5000); // Wait for the WiFi to disconnect delay(5000); // Wait for the WiFi to disconnect
@@ -250,9 +253,9 @@ void wifi_ok(){
while (WiFi.status() != WL_CONNECTED){ while (WiFi.status() != WL_CONNECTED){
delay(1000); 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){ if (millis() - start_time_recovery > 15000){
Serial.println("WiFi Connection failed to reconnect. Restarting..."); LOG_LN("WiFi Connection failed to reconnect. Restarting...");
ESP.restart(); ESP.restart();
} }
} }

View File

@@ -22,14 +22,22 @@ A ESP32-2432S028R is required to run this project. You can find out where to buy
- Extrude/Retract filament - Extrude/Retract filament
- Execute predefined gcode macros - Execute predefined gcode macros
- Toggle Moonraker power devices - Toggle Moonraker power devices
- OTA updates
- Serial console over USB (115200 8n1, echo off, LF/LF)
### Install ### 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. 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 ### Screenshots
(Quite literally shots of the screen. I'm sorry) (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) - [xtouch](https://github.com/xperiments-in/xtouch)
- [ESP32-Cheap-Yellow-Display](https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display) - [ESP32-Cheap-Yellow-Display](https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display)
- [OperatorB](https://github.com/OperatorB) for the ESP32-3248S035C display driver - [OperatorB](https://github.com/OperatorB) for the ESP32-3248S035C display driver
- [esp32-smartdisplay](https://github.com/rzeldent/esp32-smartdisplay) - [esp32-smartdisplay](https://github.com/rzeldent/esp32-smartdisplay)

View File

@@ -8,6 +8,11 @@
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
} }
TT {
font-family: 'Terminal', monospace;
background-color: #080a0b;
}
body { body {
background-color: #181a1b; background-color: #181a1b;
color: white; color: white;
@@ -27,7 +32,7 @@
color: #F44; color: #F44;
} }
.install { .configure {
margin-bottom: 300px; margin-bottom: 300px;
} }
@@ -67,7 +72,7 @@
<section class="main"> <section class="main">
<h2>CYD-Klipper <span class="iconify" data-icon="mdi-printer-3d" style="color: #F44;"></span></h2> <h2>CYD-Klipper <span class="iconify" data-icon="mdi-printer-3d" style="color: #F44;"></span></h2>
<p>An implementation of a Klipper status display on an ESP32 + screen.<br>Uses Moonraker to fetch data.<br><a href="https://github.com/suchmememanyskill/CYD-Klipper">Source code is available on GitHub</a>.</p> <p>An implementation of a Klipper status display on an ESP32 + screen.<br>Uses Moonraker to fetch data.<br><a href="https://github.com/suchmememanyskill/CYD-Klipper">Source code is available on GitHub</a>.</p>
<section class="changelog"> <section class="changelog">
<h3 id="changelog-header"><span class="iconify" data-icon="mdi-hammer-wrench" style="color: lightgray;"></span> Changelog <span id="changelog-header-version"></span></h3> <h3 id="changelog-header"><span class="iconify" data-icon="mdi-hammer-wrench" style="color: lightgray;"></span> Changelog <span id="changelog-header-version"></span></h3>
<p id="changelog-body"></p> <p id="changelog-body"></p>
@@ -97,5 +102,13 @@
</select> </select>
<span id="install-btn"></span> <span id="install-btn"></span>
</section> </section>
<section class="configure">
<h3><span class="iconify" data-icon="mdi-wrench" style="color: cyan; filter: drop-shadow(0 0 0.75rem blue);"></span> Config</h3>
<p> 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 &amp; CONSOLE" and reset the board without holding BOOT.<br>
Type <tt>help</tt> for list of available commands. You're interested in <tt>ssid</tt>, <tt>ip</tt> and <tt>key</tt>. (while you <i>can</i> set <tt>touch</tt>, it's really better done using the touchscreen.)
</section>
</section> </section>
</body> </body>