mirror of
https://github.com/suchmememanyskill/CYD-Klipper.git
synced 2026-03-21 13:43:25 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a2fff27d6 | ||
|
|
082d66ca10 | ||
|
|
41b4bff940 | ||
|
|
9136f4c94b | ||
|
|
50f4984231 | ||
|
|
48466cfb44 | ||
|
|
a7acd49d60 | ||
|
|
91920a679a | ||
|
|
53441c86c4 |
@@ -18,7 +18,8 @@ lib_deps =
|
||||
lvgl/lvgl@^8.3.9
|
||||
https://github.com/Bodmer/TFT_eSPI.git
|
||||
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
|
||||
bblanchon/ArduinoJson@^6.21.3
|
||||
bblanchon/ArduinoJson@^7.0.0
|
||||
monitor_filters = esp32_exception_decoder
|
||||
build_flags =
|
||||
-DLV_CONF_PATH="../../../../src/conf/lv_conf.h"
|
||||
-DUSER_SETUP_LOADED=1
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
GLOBAL_CONFIG global_config = {0};
|
||||
|
||||
COLOR_DEF color_defs[] = {
|
||||
{LV_PALETTE_BLUE, LV_PALETTE_RED},
|
||||
{LV_PALETTE_GREEN, LV_PALETTE_PURPLE},
|
||||
{LV_PALETTE_GREY, LV_PALETTE_CYAN},
|
||||
{LV_PALETTE_YELLOW, LV_PALETTE_PINK},
|
||||
{LV_PALETTE_ORANGE, LV_PALETTE_BLUE},
|
||||
{LV_PALETTE_RED, LV_PALETTE_GREEN},
|
||||
{LV_PALETTE_PURPLE, LV_PALETTE_GREY},
|
||||
{LV_PALETTE_BLUE, 0, LV_PALETTE_RED},
|
||||
{LV_PALETTE_LIME, -2, LV_PALETTE_PURPLE},
|
||||
{LV_PALETTE_GREY, 0, LV_PALETTE_CYAN},
|
||||
{LV_PALETTE_YELLOW, -2, LV_PALETTE_PINK},
|
||||
{LV_PALETTE_ORANGE, -2, LV_PALETTE_BLUE},
|
||||
{LV_PALETTE_RED, 0, LV_PALETTE_GREEN},
|
||||
{LV_PALETTE_PURPLE, 0, LV_PALETTE_GREY},
|
||||
};
|
||||
|
||||
void WriteGlobalConfig() {
|
||||
|
||||
@@ -10,9 +10,12 @@ typedef struct _GLOBAL_CONFIG {
|
||||
union {
|
||||
unsigned char raw;
|
||||
struct {
|
||||
// Internal
|
||||
bool screenCalibrated : 1;
|
||||
bool wifiConfigured : 1;
|
||||
bool ipConfigured : 1;
|
||||
|
||||
// External
|
||||
bool lightMode : 1;
|
||||
bool invertColors : 1;
|
||||
bool rotateScreen : 1;
|
||||
@@ -40,6 +43,7 @@ typedef struct _GLOBAL_CONFIG {
|
||||
|
||||
typedef struct _COLOR_DEF {
|
||||
lv_palette_t primary_color;
|
||||
short primary_color_light;
|
||||
lv_palette_t secondary_color;
|
||||
} COLOR_DEF;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../conf/global_config.h"
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include "macros_query.h"
|
||||
|
||||
const char *printer_state_messages[] = {
|
||||
@@ -15,8 +16,7 @@ Printer printer = {0};
|
||||
int klipper_request_consecutive_fail_count = 0;
|
||||
char filename_buff[512] = {0};
|
||||
SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore;
|
||||
long last_data_update = 0;
|
||||
const long data_update_interval = 800;
|
||||
const long data_update_interval = 780;
|
||||
|
||||
void semaphore_init(){
|
||||
freezeRenderThreadSemaphore = xSemaphoreCreateMutex();
|
||||
@@ -65,25 +65,24 @@ void send_gcode(bool wait, const char *gcode)
|
||||
|
||||
void fetch_printer_data()
|
||||
{
|
||||
bool frozen = true;
|
||||
freeze_request_thread();
|
||||
char buff[256] = {};
|
||||
sprintf(buff, "http://%s:%d/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks", global_config.klipperHost, global_config.klipperPort);
|
||||
HTTPClient client;
|
||||
client.useHTTP10(true);
|
||||
client.begin(buff);
|
||||
int httpCode = client.GET();
|
||||
delay(10);
|
||||
if (httpCode == 200)
|
||||
{
|
||||
klipper_request_consecutive_fail_count = 0;
|
||||
String payload = client.getString();
|
||||
DynamicJsonDocument doc(4096);
|
||||
deserializeJson(doc, payload);
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, client.getStream());
|
||||
auto status = doc["result"]["status"];
|
||||
bool emit_state_update = false;
|
||||
int printer_state = printer.state;
|
||||
|
||||
delay(10);
|
||||
unfreeze_request_thread();
|
||||
frozen = false;
|
||||
freeze_render_thread();
|
||||
|
||||
if (status.containsKey("webhooks"))
|
||||
@@ -196,10 +195,8 @@ void fetch_printer_data()
|
||||
{
|
||||
klipper_request_consecutive_fail_count++;
|
||||
Serial.printf("Failed to fetch printer data: %d\n", httpCode);
|
||||
}
|
||||
|
||||
if (frozen)
|
||||
unfreeze_request_thread();
|
||||
}
|
||||
}
|
||||
|
||||
void data_loop()
|
||||
@@ -211,13 +208,10 @@ void data_loop()
|
||||
}
|
||||
|
||||
void data_loop_background(void * param){
|
||||
esp_task_wdt_init(10, true);
|
||||
while (true){
|
||||
delay(100);
|
||||
if (millis() - last_data_update < data_update_interval)
|
||||
continue;
|
||||
|
||||
delay(data_update_interval);
|
||||
fetch_printer_data();
|
||||
last_data_update = millis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,5 +224,5 @@ void data_setup()
|
||||
fetch_printer_data();
|
||||
macros_query_setup();
|
||||
freeze_render_thread();
|
||||
xTaskCreatePinnedToCore(data_loop_background, "data_loop_background", 5000, NULL, 1, &background_loop, 0);
|
||||
xTaskCreatePinnedToCore(data_loop_background, "data_loop_background", 5000, NULL, 0, &background_loop, 0);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// Always has +1 entry with a null'd name
|
||||
FILESYSTEM_FILE* last_query = NULL;
|
||||
|
||||
FILESYSTEM_FILE* get_files(){
|
||||
FILESYSTEM_FILE* get_files(int limit){
|
||||
freeze_request_thread();
|
||||
|
||||
if (last_query != NULL){
|
||||
@@ -23,37 +23,76 @@ FILESYSTEM_FILE* get_files(){
|
||||
free(last_query);
|
||||
}
|
||||
|
||||
Serial.printf("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size());
|
||||
std::list<FILESYSTEM_FILE> files;
|
||||
|
||||
auto timer_request = millis();
|
||||
char buff[256] = {};
|
||||
sprintf(buff, "http://%s:%d/server/files/list", global_config.klipperHost, global_config.klipperPort);
|
||||
HTTPClient client;
|
||||
client.useHTTP10(true);
|
||||
client.begin(buff);
|
||||
int httpCode = client.GET();
|
||||
int count = 0;
|
||||
auto timer_parse = millis();
|
||||
|
||||
if (httpCode == 200){
|
||||
String payload = client.getString();
|
||||
DynamicJsonDocument doc(60000);
|
||||
auto a = deserializeJson(doc, payload);
|
||||
Serial.printf("JSON PARSE: %s\n", a.c_str());
|
||||
JsonDocument doc;
|
||||
auto parseResult = deserializeJson(doc, client.getStream());
|
||||
Serial.printf("Json parse: %s\n", parseResult.c_str());
|
||||
auto result = doc["result"].as<JsonArray>();
|
||||
|
||||
for (auto file : result){
|
||||
FILESYSTEM_FILE f = {0};
|
||||
const char* path = file["path"];
|
||||
float modified = file["modified"];
|
||||
auto file_iter = files.begin();
|
||||
|
||||
while (file_iter != files.end()){
|
||||
if ((*file_iter).modified < modified)
|
||||
break;
|
||||
|
||||
file_iter++;
|
||||
}
|
||||
|
||||
// Little inefficient as it always allocates a string, even if it doesn't have to
|
||||
f.name = (char*)malloc(strlen(path) + 1);
|
||||
if (f.name == NULL){
|
||||
Serial.println("Failed to allocate memory");
|
||||
continue;
|
||||
}
|
||||
strcpy(f.name, path);
|
||||
f.modified = file["modified"];
|
||||
files.push_back(f);
|
||||
count++;
|
||||
f.modified = modified;
|
||||
|
||||
if (file_iter != files.end())
|
||||
files.insert(file_iter, f);
|
||||
else
|
||||
files.push_back(f);
|
||||
|
||||
if (files.size() > limit){
|
||||
auto last_entry = files.back();
|
||||
|
||||
if (last_entry.name != NULL)
|
||||
free(last_entry.name);
|
||||
|
||||
files.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Serial.printf("Found %d files\n", count);
|
||||
files.sort([](FILESYSTEM_FILE a, FILESYSTEM_FILE b){return a.modified < b.modified;});
|
||||
files.reverse(); // TODO: Reverse is unneeded here, we can iterate backwards
|
||||
|
||||
size_t size = sizeof(FILESYSTEM_FILE) * (files.size() + 1);
|
||||
FILESYSTEM_FILE* result = (FILESYSTEM_FILE*)malloc(size);
|
||||
//Serial.printf("Allocated %d bytes\n", size);
|
||||
|
||||
if (result == NULL){
|
||||
Serial.println("Failed to allocate memory");
|
||||
|
||||
for (auto file : files){
|
||||
free(file.name);
|
||||
}
|
||||
|
||||
unfreeze_request_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
last_query = result;
|
||||
result[files.size()].name = NULL;
|
||||
|
||||
@@ -62,6 +101,8 @@ FILESYSTEM_FILE* get_files(){
|
||||
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);
|
||||
unfreeze_request_thread();
|
||||
return last_query;
|
||||
}
|
||||
@@ -19,4 +19,4 @@ typedef struct _FILESYSTEM_FILE {
|
||||
float modified;
|
||||
} FILESYSTEM_FILE;
|
||||
|
||||
FILESYSTEM_FILE* get_files();
|
||||
FILESYSTEM_FILE* get_files(int limit);
|
||||
@@ -15,12 +15,12 @@ static void on_state_change(void * s, lv_msg_t * m) {
|
||||
|
||||
String url = "http://" + String(global_config.klipperHost) + ":" + String(global_config.klipperPort) + "/printer/gcode/help";
|
||||
HTTPClient client;
|
||||
client.useHTTP10(true);
|
||||
client.begin(url.c_str());
|
||||
int httpCode = client.GET();
|
||||
if (httpCode == 200){
|
||||
String payload = client.getString();
|
||||
DynamicJsonDocument doc(16384);
|
||||
deserializeJson(doc, payload);
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, client.getStream());
|
||||
auto result = doc["result"].as<JsonObject>();
|
||||
|
||||
for (int i = 0; i < macros_count; i++){
|
||||
|
||||
@@ -84,7 +84,11 @@ void touchscreen_calibrate(bool force)
|
||||
|
||||
void screen_setBrightness(byte brightness)
|
||||
{
|
||||
analogWrite(TFT_BL, brightness);
|
||||
// calculate duty, 4095 from 2 ^ 12 - 1
|
||||
uint32_t duty = (4095 / 255) * brightness;
|
||||
|
||||
// write duty to LEDC
|
||||
ledcWrite(0, duty);
|
||||
}
|
||||
|
||||
void set_screen_brightness()
|
||||
@@ -183,7 +187,20 @@ void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
|
||||
|
||||
void set_color_scheme(){
|
||||
lv_disp_t *dispp = lv_disp_get_default();
|
||||
lv_theme_t *theme = lv_theme_default_init(dispp, lv_palette_main(color_defs[global_config.color_scheme].primary_color), lv_palette_main(color_defs[global_config.color_scheme].secondary_color), !global_config.lightMode, LV_FONT_DEFAULT);
|
||||
lv_color_t main_color = {0};
|
||||
COLOR_DEF color_def = color_defs[global_config.color_scheme];
|
||||
|
||||
if (color_defs[global_config.color_scheme].primary_color_light > 0){
|
||||
main_color = lv_palette_lighten(color_def.primary_color, color_def.primary_color_light);
|
||||
}
|
||||
else if (color_defs[global_config.color_scheme].primary_color_light < 0) {
|
||||
main_color = lv_palette_darken(color_def.primary_color, color_def.primary_color_light * -1);
|
||||
}
|
||||
else {
|
||||
main_color = lv_palette_main(color_defs[global_config.color_scheme].primary_color);
|
||||
}
|
||||
|
||||
lv_theme_t *theme = lv_theme_default_init(dispp, main_color, lv_palette_main(color_def.secondary_color), !global_config.lightMode, LV_FONT_DEFAULT);
|
||||
lv_disp_set_theme(dispp, theme);
|
||||
}
|
||||
|
||||
@@ -200,6 +217,10 @@ void screen_setup()
|
||||
lv_init();
|
||||
|
||||
tft.init();
|
||||
|
||||
ledcSetup(0, 5000, 12);
|
||||
ledcAttachPin(21, 0);
|
||||
|
||||
tft.setRotation(global_config.rotateScreen ? 3 : 1);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
set_screen_brightness();
|
||||
|
||||
@@ -95,13 +95,20 @@ void print_panel_init(lv_obj_t* panel){
|
||||
lv_obj_set_size(list, panel_width_margin, panel_height_margin);
|
||||
lv_obj_align(list, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
FILESYSTEM_FILE* files = get_files();
|
||||
FILESYSTEM_FILE* files = get_files(25);
|
||||
int count = 0;
|
||||
while (files->name != NULL && count <= 20){
|
||||
while (files != NULL && files->name != NULL && count <= 20){
|
||||
lv_obj_t * btn = lv_list_add_btn(list, LV_SYMBOL_FILE, files->name);
|
||||
lv_obj_add_event_cb(btn, btn_print_file_verify, LV_EVENT_CLICKED, (void*)files);
|
||||
|
||||
files += 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count <= 0){
|
||||
lv_obj_del(list);
|
||||
lv_obj_t * label = lv_label_create(panel);
|
||||
lv_label_set_text(label, "Failed to read files.");
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
<p>An implementation of a Klipper status display on an ESP32 + screen.<br>Uses Moonraker to fetch data.</p>
|
||||
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/suchmememanyskill/CYD-Klipper">
|
||||
<a href="https://github.com/suchmememanyskill/CYD-Klipper"><img alt="GitHub repo" src="https://img.shields.io/badge/Source-Github-blue.svg"></a>
|
||||
<a href="https://ko-fi.com/suchmememanyskill"><img alt="Donate KoFi" src="https://img.shields.io/badge/Support%2FDonate%20On-Ko%20Fi-red"></a>
|
||||
<section class="install">
|
||||
<h3>Install</h3>
|
||||
<p>Note: You may need to hold the 'BOOT' button on the device while pressing install</p>
|
||||
|
||||
Reference in New Issue
Block a user