296 Commits
v1.0 ... v2.1.0

Author SHA1 Message Date
Sims
e6b5d17f6a Merge pull request #164 from suchmememanyskill/dev
v2.1.0
2025-02-01 19:39:18 +01:00
suchmememanyskill
d8a9b13fe1 Fix compile 2025-02-01 17:37:06 +01:00
suchmememanyskill
fbc2964ad8 Make serial script work with both sudo and not sudo 2025-02-01 17:20:06 +01:00
suchmememanyskill
d616ef5a6d Allow running service under root 2025-01-28 21:50:21 +01:00
suchmememanyskill
9bfbb1cb0e Change keyboard for bambu setup (#161) 2025-01-28 21:23:02 +01:00
suchmememanyskill
8d8e36fde9 Merge #152 2025-01-28 21:20:22 +01:00
Kire Dyfvelsten
4e3cd69b63 Sunton esp32 2.2" c (#151)
* Update readme

* Update README.md

* driver definition? for a vertical 2.2" with touch

* Update platformio.ini

* Update esp32-2432S022C-vertical.json

* Update esp32-2432S022C-vertical.json

---------

Co-authored-by: Sims <38142618+suchmememanyskill@users.noreply.github.com>
2025-01-28 21:19:36 +01:00
Sims
3aeb67cfdd esp32-CROWPANEL-35C (#159)
* :)

* Correct screen rotation

* Remove brightness adjustment

* Silence buzzer

* Fixes from atomique
2025-01-28 21:16:46 +01:00
suchmememanyskill
8f46b9972d Add aliexpress links 2025-01-15 22:18:36 +01:00
suchmememanyskill
0992f466cf Fix Klipper params being non-blocking 2025-01-12 00:13:26 +01:00
suchmememanyskill
f8ed3afd06 Don't ignore first popup message (Klipper) 2025-01-11 23:50:58 +01:00
suchmememanyskill
fdfa2d014f Properly clear input buffer for serial 2025-01-11 17:15:26 +01:00
suchmememanyskill
1efaa616e1 Add 2x scale gcode preview option 2025-01-10 22:28:35 +01:00
Sims
5756b31744 Update README.md 2025-01-10 18:15:56 +01:00
suchmememanyskill
99b70622fe Update readme 2025-01-09 23:14:15 +01:00
Sims
b3d405b355 Merge pull request #139 from suchmememanyskill/dev
v2.0.0 - Happy new year!
2025-01-09 23:05:35 +01:00
Sims
1cd6c8fd92 Merge pull request #131 from suchmememanyskill/printer-interface
Seperate backend from frontend, abstract printer implementation
2025-01-09 22:35:32 +01:00
suchmememanyskill
7b0497c75b Fix crowpanel compile 2025-01-09 22:35:13 +01:00
Sims
df610168c0 Merge branch 'dev' into printer-interface 2025-01-09 22:16:43 +01:00
Madrajib Lab
d0f90a56d0 Add support for crowpanel 2.8inch display (#134)
* Add support for crowpanel 2.8inch display

* Add support for crowpanel 2.8inch display

---------

Co-authored-by: Sims <38142618+suchmememanyskill@users.noreply.github.com>
2025-01-09 22:15:03 +01:00
suchmememanyskill
b67f091c46 Add alt for esp32-8048S043C-SD (#135) 2025-01-09 22:12:05 +01:00
suchmememanyskill
71119e1648 Make UI more specific per printer type, hide settings 2025-01-09 22:00:53 +01:00
suchmememanyskill
660723d596 Fix arduinojson compile 2025-01-09 21:12:50 +01:00
suchmememanyskill
5ec2ba66e4 Change some spelling, set timeout on bambu loop 2024-12-22 23:20:36 +01:00
suchmememanyskill
4fc16ecc92 Fan profiles: On to 100% 2024-12-22 23:04:12 +01:00
suchmememanyskill
161c10a43b Start print in bambu integration 2024-12-22 00:14:49 +01:00
suchmememanyskill
093dd5efff Add fan/speed controls to bambu 2024-12-21 22:04:51 +01:00
Sims
4dd70aa3ea Add bambu panels 2024-12-19 18:17:40 +01:00
suchmememanyskill
f4498e95cd Make octoprint work after testing 2024-12-15 22:06:01 +01:00
suchmememanyskill
b342e58cfc Install python packages in venv 2024-12-15 13:10:12 +01:00
suchmememanyskill
211fe15aee Fix compile errors 2024-12-15 12:13:32 +01:00
Sims
abbb0b2f7e We have 3 2024-12-15 01:16:46 +01:00
Sims
6aa74633a8 Add min fetcher for octoprint 2024-12-15 01:15:58 +01:00
Sims
2f1b0095b0 Implement missing commands for octoprint 2024-12-15 00:59:06 +01:00
Sims
68b68af715 Add octoprint panels 2024-12-15 00:40:11 +01:00
Sims
96f8695b0e Continued impl of octoprint 2024-12-11 17:48:22 +01:00
Sims
2fce5a9e30 Start implementation on file list 2024-12-11 08:29:18 +01:00
Sims
1578b4129d Better better split impl 2024-12-11 08:10:05 +01:00
Sims
fcd133eb32 Better split impl 2024-12-11 08:00:45 +01:00
Sims
15dedb7255 Octoprint: Add more commands 2024-12-04 08:27:41 +01:00
suchmememanyskill
0c364ec597 Lower cpu usage 2024-11-27 22:53:35 +01:00
suchmememanyskill
8521664a69 Fetch data from octoprint 2024-11-16 22:51:17 +01:00
suchmememanyskill
1b0ca807e4 Fix bed temp set, start octoprint impl 2024-11-16 12:41:06 +01:00
suchmememanyskill
12781051ca Serial installer 2024-11-10 00:54:38 +01:00
suchmememanyskill
e5390e731f Allow accessing device settings from wifi panel 2024-11-09 12:30:38 +01:00
suchmememanyskill
17143d2878 Allow running wifi-less 2024-11-09 02:06:04 +01:00
suchmememanyskill
bf90803107 Remove serial semaphore 2024-11-09 01:37:48 +01:00
suchmememanyskill
f9444829ee Gcode images, fixes to serial 2024-11-09 01:30:40 +01:00
suchmememanyskill
92b7b68dc5 Get Serial to a functional state 2024-11-09 00:31:03 +01:00
suchmememanyskill
747ff4c79c More serial 2024-11-09 00:09:48 +01:00
Sims
d4ce7a71ba Klipper connection over serial 2024-11-08 18:07:32 +01:00
suchmememanyskill
0e57aed87c Add bambu file loading 2024-11-04 21:47:02 +01:00
suchmememanyskill
66e3a6765c Fix time in bambu integration 2024-11-02 14:42:14 +01:00
suchmememanyskill
f21b480611 Scale numeric input to 75% screen 2024-10-30 01:26:11 +01:00
suchmememanyskill
49a1bd6e57 Try windows compilation 2024-10-29 21:05:38 +01:00
suchmememanyskill
1483345e67 Test 2024-10-29 18:57:35 +01:00
suchmememanyskill
205926364c A 2024-10-29 18:30:27 +01:00
suchmememanyskill
9a915bedda Explicitly define wifi client 2024-10-29 18:18:57 +01:00
suchmememanyskill
650b055fcd Attempt to fix CI 2024-10-29 18:14:52 +01:00
suchmememanyskill
4207d82a7e Basic bambu implementation 2024-10-29 17:57:20 +01:00
suchmememanyskill
b1138d9510 Split json parsing from printer impl 2024-10-28 15:33:17 +01:00
suchmememanyskill
572d27dc8a Update serial comms 2024-10-28 14:00:53 +01:00
suchmememanyskill
ccc5817799 More fixes 2024-10-28 13:57:18 +01:00
suchmememanyskill
b99e00d79c Fixes 2024-10-28 01:23:07 +01:00
suchmememanyskill
d6e788f0c8 Fix some warnings 2024-10-27 22:15:39 +01:00
suchmememanyskill
5bb4fd2b3c Fix virtual definition 2024-10-27 22:06:12 +01:00
suchmememanyskill
69d98a9e19 Fix serial console printer switching 2024-10-27 22:00:20 +01:00
suchmememanyskill
1516b785f5 A 2024-10-27 21:55:41 +01:00
suchmememanyskill
fcdce6c5bf A 2024-10-27 21:49:43 +01:00
suchmememanyskill
c0e329c57d :) 2024-10-27 20:39:51 +01:00
suchmememanyskill
6273e10e5a More 2024-10-27 19:08:21 +01:00
suchmememanyskill
75bb334b09 Use new image system 2024-10-27 17:15:46 +01:00
suchmememanyskill
be0bf0fc71 Printer init 2024-10-27 15:23:25 +01:00
suchmememanyskill
c35b146762 Explicit cast to void* 2024-10-27 01:53:00 +02:00
suchmememanyskill
e55c2871d9 Finish all panels 2024-10-27 01:46:31 +02:00
suchmememanyskill
309a6865a5 More progress 2024-10-27 00:06:37 +02:00
Sims
ec7e6f180d Implement fetchdatamin 2024-10-25 08:28:26 +02:00
suchmememanyskill
337a26be3d Start integration 2024-10-24 23:08:03 +02:00
Sims
6a9023eb8d Implement klipper custom ui panels 2024-10-24 18:18:15 +02:00
suchmememanyskill
2078a1541d More work 2024-10-23 23:15:43 +02:00
suchmememanyskill
eed4b3efef Cleanup 2024-10-22 22:56:04 +02:00
suchmememanyskill
22fc25ccc7 Port more 2024-10-21 23:08:53 +02:00
suchmememanyskill
9a6fce854c In progress 2024-10-20 22:27:31 +02:00
Sims
9a96f9336f Merge pull request #125 from suchmememanyskill/dev
v1.8.0
2024-08-31 11:49:48 +02:00
Rory Hayes
0b1db1d834 Add emergency stop and full filename options (#124)
* init estop button

* style estop and add full filename option

* finalize estop style and adjust layout to accommodate extras

---------

Co-authored-by: Sims <38142618+suchmememanyskill@users.noreply.github.com>
2024-08-31 11:36:00 +02:00
suchmememanyskill
db019939a6 Allow escaped spaces in wifi ssid serial console 2024-08-14 23:06:46 +02:00
Sims
5c46764c7c Merge pull request #122 from suchmememanyskill/dev
v1.7.0
2024-08-02 21:39:42 +02:00
suchmememanyskill
6ebaf68cf1 Merge branch 'master' into dev 2024-08-02 21:19:47 +02:00
bwucke
41aa073ae0 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>
2024-08-02 21:16:45 +02:00
suchmememanyskill
19cfaefd36 Don't forget to actually use the auth 2024-07-14 19:57:43 +02:00
suchmememanyskill
04d890227e Fix auth on minimal requests 2024-07-14 19:54:38 +02:00
Sims
e2c2a38b20 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>
2024-06-28 19:11:20 +02:00
Miroslav Zuzelka
5d2571ef83 Update ci.py (#110)
Typo fix for ESP32-S3 boards array name
2024-06-12 11:27:08 +02:00
suchmememanyskill
d4645f4fa1 Add files menu to params panel while printing (implement #80) 2024-06-11 20:52:51 +02:00
suchmememanyskill
4ac87c8ffc Set chip family to ESP32-S3 for specific models (fix #67) 2024-06-11 20:49:50 +02:00
suchmememanyskill
d780c8d55e Implement file sorting (implement #89) 2024-06-11 20:49:13 +02:00
Sebastian Göls
d69446a11b Bulletproof ci.py (#107) 2024-06-10 23:06:44 +02:00
Sebastian Göls
90fd1b0ab2 Add .gitignore file (#108) 2024-06-10 22:42:44 +02:00
suchmememanyskill
4a96f7db0b Fix gcode previews with special chars not loading 2024-06-09 00:18:57 +02:00
Sims
c640d7fade Merge pull request #104 from suchmememanyskill/dev
Clear memory before doing OTA
2024-05-28 22:15:23 +02:00
suchmememanyskill
d22a9e1ee4 Clear memory before doing OTA 2024-05-28 22:03:51 +02:00
Sims
98c7364ce7 Merge pull request #103 from suchmememanyskill/dev
v1.6.2
2024-05-28 21:30:19 +02:00
suchmememanyskill
7815a0fbf4 Specify specific version for esp32 platform 2024-05-26 00:26:49 +02:00
suchmememanyskill
bd32fcb81e Allow accessing the parameters/stats panel from unlocked stepper move panel 2024-05-25 12:47:43 +02:00
suchmememanyskill
bc0502745d Allow custom extrude/retract macros 2024-05-25 12:40:07 +02:00
suchmememanyskill
d75cbb65dc Fix #99 2024-05-11 00:05:24 +02:00
Beebles
e004456ee9 Update README.md (#95) 2024-04-28 23:57:29 +02:00
Flaviu Tamas
ae34e91530 Custom API key keymap (#94)
Since the api key is just hex, we can use a keymap with larger buttons
that is easier to use.
2024-04-28 20:23:49 +02:00
Flaviu Tamas
cbd40414c8 Fix segfault on entering IP (#93)
On my esp32-3248S035C, after entering the IP address, I was seeing a
segfault at

lv_mem_free at .pio/libdeps/esp32-3248S035C/lvgl/src/misc/lv_mem.c:179
allocate_btn_areas_and_controls at .pio/libdeps/esp32-3248S035C/lvgl/src/widgets/lv_btnmatrix.c:877
lv_btnmatrix_set_map at .pio/libdeps/esp32-3248S035C/lvgl/src/widgets/lv_btnmatrix.c:94
lv_keyboard_update_map at .pio/libdeps/esp32-3248S035C/lvgl/src/extra/widgets/keyboard/lv_keyboard.c:397
lv_keyboard_set_mode at .pio/libdeps/esp32-3248S035C/lvgl/src/extra/widgets/keyboard/lv_keyboard.c:185
keyboard_event_ip_entry(_lv_event_t*) at src/ui/ip_setup.cpp:81
event_send_core at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:467
lv_event_send at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:63
lv_keyboard_def_event_cb at .pio/libdeps/esp32-3248S035C/lvgl/src/extra/widgets/keyboard/lv_keyboard.c:308
event_send_core at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:467
lv_event_send at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:63
lv_btnmatrix_event at .pio/libdeps/esp32-3248S035C/lvgl/src/widgets/lv_btnmatrix.c:520
lv_obj_event_base at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:91 (discriminator 1)
event_send_core at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:458
lv_event_send at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_event.c:63
indev_proc_release at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_indev.c:970
indev_button_proc at .pio/libdeps/esp32-3248S035C/lvgl/src/core/lv_indev.c:808
lv_timer_exec at .pio/libdeps/esp32-3248S035C/lvgl/src/misc/lv_timer.c:313
set_screen_brightness() at src/core/lv_setup.cpp:191
ip_init() at src/ui/ip_setup.cpp:239
setup() at src/main.cpp:28
loopTask(void*) at /home/user/etc/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:41

This seems to be due to a free-after-free. The solution here is to
potentially change the keymap before running the rest of the logic,
because the later logic may tear down the context.

This change also only changes the keyboard mode on focus change, to
avoid running this code on the many un-related events that this handler
gets called for.
2024-04-28 20:22:06 +02:00
suchmememanyskill
06691df094 Remove esp32-4827S043R-SD from CI builds 2024-04-24 22:23:57 +02:00
suchmememanyskill
9b551915d7 Fix possible nullref in fetch_printer_data 2024-04-15 22:56:57 +02:00
suchmememanyskill
73be7c6c9f Forgot to add device to platformio cfg 2024-04-15 19:39:03 +02:00
suchmememanyskill
e06ea214c4 esp32-4827S043R 2024-04-15 18:02:05 +02:00
suchmememanyskill
9e739de731 Add more clear manual install instructions 2024-04-15 17:37:07 +02:00
Sims
29dcb4717a Merge pull request #87 from suchmememanyskill/dev
v1.6.1
2024-04-14 12:13:50 +02:00
suchmememanyskill
356c78ee5f Lock UI thread when setting state to offline, fix #86 2024-04-14 12:01:35 +02:00
suchmememanyskill
dce6f70ef9 Allow dismissing m117 messages, allow disabling m117 messages 2024-04-05 23:03:48 +02:00
Sims
2e3ac7b02c Merge pull request #83 from suchmememanyskill/dev
v1.6.0
2024-03-29 22:51:31 +01:00
suchmememanyskill
2e5a2dfbeb Add note about clearing touch calibration 2024-03-29 22:14:21 +01:00
suchmememanyskill
4a4fdb77d6 Clarify invert colors text 2024-03-29 22:07:14 +01:00
suchmememanyskill
db448ae401 Fix compile error 2024-03-29 21:26:23 +01:00
suchmememanyskill
9a9134da4a Reset touch calibration when holding BOOT for 8 seconds 2024-03-29 20:16:13 +01:00
suchmememanyskill
86a999253f Reorganise settings menu 2024-03-28 20:26:57 +01:00
suchmememanyskill
1238b7ee37 Add color fix for dual-usb 2432S028R 2024-03-28 18:25:32 +01:00
suchmememanyskill
f110feee1e Copy unstable manual ssid string 2024-03-27 17:37:25 +01:00
suchmememanyskill
41d0b77d17 Copy over move data from previous printer config 2024-03-26 23:16:51 +01:00
suchmememanyskill
364f1ee49c Add configurable move values 2024-03-26 22:57:54 +01:00
Kurt Haenen
ad68095124 Differentiated between printed and elapsed time (#75)
Co-authored-by: Kurt Haenen <kurt.haenen@quintux.com>
2024-03-26 21:13:53 +01:00
suchmememanyskill
38a1acb7b1 Refactor wifi setup; add option to type ssid manually 2024-03-26 20:05:58 +01:00
suchmememanyskill
f5f970afce Show macros help message even when there's power devices present 2024-03-26 18:51:25 +01:00
suchmememanyskill
e4dd146a96 Fix slicer estimate when switching between printers 2024-03-25 22:02:00 +01:00
suchmememanyskill
ea8a6b561f Allow setting absolute xyz pos 2024-03-24 18:10:22 +01:00
suchmememanyskill
a9a732daa6 Revert coloring active printer buttons; Put wifi icon instead 2024-03-24 00:28:58 +01:00
suchmememanyskill
713e04bfa5 Improve printer menu 2024-03-22 22:09:09 +01:00
suchmememanyskill
0f472ae78b Don't show error screen on null'd status 2024-03-22 21:15:00 +01:00
suchmememanyskill
400e2ae266 Use display_status for print progress 2024-03-22 21:13:02 +01:00
suchmememanyskill
315e066e27 Allow passwordless wifi networks; reconnect to wifi network once 2024-03-21 22:00:24 +01:00
suchmememanyskill
c7c6b26730 Move ip connect to panel; add restart to wifi 2024-03-20 22:24:06 +01:00
suchmememanyskill
6cde9cb887 Add printer switch to ip setup screen 2024-03-18 18:05:08 +01:00
suchmememanyskill
44e57995fb Fix lines in ESP32-2432S024 2024-03-18 17:41:53 +01:00
suchmememanyskill
56301d3d72 Show extra stats on progress panel 2024-03-17 14:59:43 +01:00
suchmememanyskill
ae3348d61e Fix a bit of wording 2024-03-16 11:02:24 +01:00
Sims
4bfe149244 Merge pull request #66 from suchmememanyskill/dev
v1.5.0
2024-03-15 22:42:34 +01:00
suchmememanyskill
7aceb85621 Move error ui to panel 2024-03-14 19:00:57 +01:00
suchmememanyskill
9c958b42b3 Fix macro panel in error screen 2024-03-14 18:28:22 +01:00
suchmememanyskill
92d47d8c07 Move print preview image to the top left 2024-03-13 22:42:06 +01:00
suchmememanyskill
1a31ef0758 map error text in moonraker as error state 2024-03-13 22:26:10 +01:00
suchmememanyskill
a7bde99442 Fix inverted vertical 2024-03-13 20:20:57 +01:00
suchmememanyskill
be3b2ddb24 3248S035C Vertical 2024-03-13 19:50:20 +01:00
suchmememanyskill
36b37176d6 Fix macros tab controlling other printers 2024-03-13 18:46:13 +01:00
suchmememanyskill
d0d80e8980 Imrpove site 2024-03-12 22:06:52 +01:00
suchmememanyskill
65abe295c9 Fix button disable conditions 2024-03-12 21:30:23 +01:00
suchmememanyskill
a8c94fe207 Refactor + Power menu in printer menu 2024-03-12 21:08:48 +01:00
suchmememanyskill
a265301d97 Update smartdisplay driver to v2.0.7 2024-03-11 22:20:56 +01:00
suchmememanyskill
90dcf95cf6 Merge branch 'experimental-multi-printer' into dev 2024-03-11 21:55:44 +01:00
suchmememanyskill
8d6d22c38a Change status to 'In Control' when in control 2024-03-11 21:55:33 +01:00
suchmememanyskill
fb1e264df7 Display M117 message on screen 2024-03-10 01:08:47 +01:00
suchmememanyskill
e457114402 (Hopefully) fix touch on 2432S024C 2024-03-10 00:03:30 +01:00
suchmememanyskill
61f15ff6f3 Fix bugs 2024-03-09 21:43:46 +01:00
suchmememanyskill
451304b5df Add 2432S024C and 4827S043C to CI 2024-03-09 21:16:55 +01:00
suchmememanyskill
68ff78eb49 Add 2432S024C-smartdisplay 2024-03-09 21:16:40 +01:00
Andrey Melnikov
215439df2f adds esp32-4827s043C-smartdisplay (#63)
* +add esp32-4827s043C

* DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX 40 to 35
2024-03-09 21:10:39 +01:00
suchmememanyskill
397c835129 Add build instructions 2024-03-09 21:08:45 +01:00
suchmememanyskill
870d109c92 Implement printer switching in ip connect/error screen 2024-03-09 20:47:45 +01:00
suchmememanyskill
f98c2eff78 Small refactor 2024-03-09 19:22:24 +01:00
suchmememanyskill
67b5ce1948 Set max printer name limit to 24 chars 2024-03-09 01:56:35 +01:00
suchmememanyskill
e9b58e0a6f Implement printer panel fully 2024-03-09 01:55:31 +01:00
suchmememanyskill
3b7b49c62b Split configuration; start printer panel 2024-03-07 23:25:12 +01:00
suchmememanyskill
f0cc211e30 Unify http client creation 2024-03-06 22:32:49 +01:00
suchmememanyskill
9427381e05 Add other CYD 2.8R inch models for smartdisplay 2024-03-03 01:02:52 +01:00
suchmememanyskill
02e27e6d83 Set screen brightness in lvgl setup 2024-03-03 01:00:09 +01:00
suchmememanyskill
b65003e40b Better button sizes for esp32-3248S035C 2024-03-02 23:50:24 +01:00
suchmememanyskill
cb242240d3 Remove unneeded code 2024-03-02 22:11:39 +01:00
suchmememanyskill
c061164edf Fix compile on linux 2024-03-02 22:06:15 +01:00
suchmememanyskill
ea42bf775d Merge branch 'experimental_load_gcode_img-2' into dev 2024-03-02 22:03:14 +01:00
suchmememanyskill
4e6457c729 Misc fixes 2024-03-02 22:02:20 +01:00
themacboy
bc3ca3892f Some visual UI tweaks for ESP32-8048S043C (#57)
* Some visual UI tweaks

* more visual UI tweaks adjustements
2024-03-02 21:57:00 +01:00
suchmememanyskill
639eb50371 A 2024-03-02 21:16:31 +01:00
suchmememanyskill
1adb966ee1 Image load round 2 (slightly less unstable) 2024-03-02 21:05:16 +01:00
suchmememanyskill
64290afd89 Set size for settings dropdown 2024-03-02 14:18:37 +01:00
suchmememanyskill
3fbd14f154 Hopefully fix esp32-8048S043C 2024-03-02 10:38:34 +01:00
suchmememanyskill
ef3676faef Merge branch 'dev' into 4.3inch 2024-03-02 10:32:14 +01:00
suchmememanyskill
fc7cfbd85b Add esp32-2432S032C to CI 2024-03-02 10:22:18 +01:00
suchmememanyskill
49c27f2b01 Add esp32-2432S032C 2024-03-02 10:21:09 +01:00
suchmememanyskill
801432c3a6 Driver configuration improvements 2024-03-02 10:19:03 +01:00
suchmememanyskill
55e9ce3d81 Vertical UI, round 2 2024-03-01 22:28:35 +01:00
suchmememanyskill
f467e8a604 Add esp32-8048S043C 2024-02-27 21:32:00 +01:00
suchmememanyskill
45779b5a13 Make calibration work with smartdisplay driver 2024-02-24 14:27:03 +01:00
Sims
1c7d5ffacd Merge pull request #51 from suchmememanyskill/dev
v1.4.0
2024-02-21 23:33:13 +01:00
suchmememanyskill
1bc770a1cf Undo o2 optimalisations 2024-02-21 23:20:01 +01:00
suchmememanyskill
b5ac6d045a Retry slicer print time fetch 2024-02-20 23:20:33 +01:00
suchmememanyskill
c9f8935310 Don't send gcode requests twice 2024-02-20 22:40:00 +01:00
suchmememanyskill
ff1c13602c Add API key flow 2024-02-19 21:56:35 +01:00
Flaviu Tamas
5d3d32b116 Fix frequent null-pointer errors (#49)
I was frequently seeing the following and a reboot:

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x4008ae2d  PS      : 0x00060330  A0      : 0x800d3fd9  A1      : 0x3ffe15f0
A2      : 0x3ffc40e8  A3      : 0x00000000  A4      : 0x000000ff  A5      : 0x0000ff00
A6      : 0x00ff0000  A7      : 0xff000000  A8      : 0x00000000  A9      : 0x3ffe15c0
A10     : 0x3ffc40e8  A11     : 0x3f40035d  A12     : 0x3ffe16a0  A13     : 0x000004b0
A14     : 0x7ff00000  A15     : 0x7ff2c000  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000  LBEG    : 0x4008ae2d  LEND    : 0x4008ae41  LCOUNT  : 0xffffffff

Backtrace: 0x4008ae2a:0x3ffe15f0 0x400d3fd6:0x3ffe1600 0x400d42d3:0x3ffe18f0

decoded:

0x4008acbc: strcmp at /builds/idf/crosstool-NG/.build/xtensa-esp32-elf/src/newlib/newlib/libc/machine/xtensa/strcmp.S:467
0x400d4145: fetch_printer_data() at /home/user/tmp/CYD-Klipper/CYD-Klipper/src/core/data_setup.cpp:180
0x400d42d3: data_loop_background(void*) at /home/user/tmp/CYD-Klipper/CYD-Klipper/src/core/data_setup.cpp:233 (discriminator 1)

Seems like the printer state was frequently ending up as null, so check
for that and return a reasonable response. After applying this patch,
things are stable for me.
2024-02-19 21:26:22 +01:00
suchmememanyskill
edec1724e6 Properly fix #45 2024-02-19 17:37:08 +01:00
suchmememanyskill
dd5e82d637 Reorder settings 2024-02-19 17:16:56 +01:00
suchmememanyskill
e5edabffa4 Add slicer time based estimates 2024-02-18 12:04:01 +01:00
suchmememanyskill
a1bb6a750f Improve Z Offset menu 2024-02-17 12:11:18 +01:00
suchmememanyskill
43cefaafd2 Make temp chart fit the lines 2024-02-16 23:39:35 +01:00
suchmememanyskill
dcf32d6685 Fix temp chart crashing 2024-02-16 23:09:32 +01:00
suchmememanyskill
12c4319173 Add graph to temp panel 2024-02-16 22:47:55 +01:00
suchmememanyskill
86be30034d Optimalisations 2024-02-16 22:15:15 +01:00
suchmememanyskill
4fc2316970 (Hack) Restart ESP when klipper calls start failing 2024-02-15 18:32:36 +01:00
suchmememanyskill
3dc241dbec Make stats panel accessible from move panel, improve Z offset menu 2024-02-15 18:29:13 +01:00
suchmememanyskill
cd58fcae4f Move touch calibration to lv_conf, implement invert colors in smartdisplay driver 2024-02-12 22:26:09 +01:00
suchmememanyskill
87a9257e19 Add ESP32-2432S022C using smartdisplay driver 2024-02-12 18:24:30 +01:00
suchmememanyskill
a436c6b5c7 Update readme 2024-02-11 10:33:22 +01:00
Sims
2b92b8daee Merge pull request #37 from suchmememanyskill/dev
v1.3.0
2024-02-11 00:23:56 +01:00
suchmememanyskill
c443bb74d7 increase 3.5" screens SPI freq to 80mhz 2024-02-10 23:24:24 +01:00
suchmememanyskill
2fb83df0cf Set default font in a better way 2024-02-10 13:11:11 +01:00
suchmememanyskill
1c10d46a5e Make temp manual entry ui responsive 2024-02-10 13:02:40 +01:00
suchmememanyskill
d3e7eec47a Reset config, fix screen calibration 2024-02-10 12:09:43 +01:00
suchmememanyskill
64266b1ff8 Implement OTA updates 2024-02-10 00:37:25 +01:00
suchmememanyskill
254d8453ad A 2024-02-09 22:12:51 +01:00
suchmememanyskill
f05246f8c7 Ignore tags during build 2024-02-09 22:11:54 +01:00
suchmememanyskill
5bbdc9e509 Fix version extractor 2024-02-09 21:51:24 +01:00
suchmememanyskill
4302c4492c More CI changes 2024-02-09 21:44:21 +01:00
suchmememanyskill
8ff2be168e Fetch entire git history so tag can be extracted 2024-02-09 21:28:17 +01:00
suchmememanyskill
4fe6f3e975 State device name in webinstall tool 2024-02-09 21:22:56 +01:00
Sims
b8cb7f7ad9 Merge pull request #34 from suchmememanyskill/dev
v1.2.0: More displays, improved ui, unified ui
2024-02-09 21:15:05 +01:00
suchmememanyskill
15209544d0 Add version to settings 2024-02-09 21:06:23 +01:00
suchmememanyskill
452dbefbdb Is this why linux CI broke? 2024-02-09 20:13:28 +01:00
suchmememanyskill
adea917a5d html changes 2024-02-09 19:19:20 +01:00
Matt Hawley
e5efe08fc3 Updating keyboard to match style / layout of original (#33) 2024-02-09 19:10:33 +01:00
suchmememanyskill
2c7aab7607 Shuffle around board defines 2024-02-09 19:05:16 +01:00
suchmememanyskill
4daa0aa549 Extract display timeout from driver 2024-02-09 18:53:17 +01:00
suchmememanyskill
134e7ad48d Remove reference to boards 2024-02-09 17:47:29 +01:00
suchmememanyskill
2d6c2e8d10 Fix up smartdisplay driver, update readme, update install page 2024-02-08 23:35:31 +01:00
suchmememanyskill
725d76d9c5 Modify CI further 2024-02-07 23:39:25 +01:00
suchmememanyskill
e669017949 I hate CI 2024-02-07 23:07:44 +01:00
suchmememanyskill
82b1d515d1 Edit CI 2024-02-07 23:02:28 +01:00
suchmememanyskill
f985813960 Update boards 2024-02-07 22:37:52 +01:00
suchmememanyskill
81aa08b732 Delete boards 2024-02-07 22:25:26 +01:00
suchmememanyskill
fb928d28db Fix broken auto sleep 2024-02-07 22:10:04 +01:00
suchmememanyskill
716f934b6c Fix scaling for 3.5inch, Fix text scaling in general 2024-02-07 21:58:23 +01:00
suchmememanyskill
26a0f35dd2 Merge branch 'setup-ports' of https://github.com/suchmememanyskill/CYD-Klipper-Display into setup-ports 2024-02-07 21:04:01 +01:00
suchmememanyskill
ab4688c6dd Initial port to ESP32_3248S035C 2024-02-07 21:03:26 +01:00
Matt Hawley
c18cd10c69 Adding support for hostname with dual keyboards for port (#29)
* Adding support for hostname with dual keyboards for port

* Updating to use a single keyboard

* Changing keyboard layout
2024-02-06 17:30:59 +01:00
suchmememanyskill
1f76012423 Make power devices also show on klipper connect screen if available 2024-02-03 23:45:28 +01:00
suchmememanyskill
a07d28293c Add power devices to macro menu 2024-02-03 22:41:31 +01:00
suchmememanyskill
e152868e0f Add macros to not ready screen 2024-02-03 17:22:09 +01:00
suchmememanyskill
77db3652f2 Add info to stat panel 2024-02-03 16:39:49 +01:00
suchmememanyskill
41be4b1a31 Add stats panel 2024-02-03 15:16:25 +01:00
suchmememanyskill
ba015bb2e8 Switch to fork of lvgl for keyboard mods 2024-02-01 18:18:36 +01:00
suchmememanyskill
1ff75d2aa5 Process feedback partially 2024-02-01 18:10:40 +01:00
suchmememanyskill
4e7bff92c9 Test CI 2 2024-01-30 20:50:35 +01:00
suchmememanyskill
9c12588187 CI pls 2024-01-30 20:47:56 +01:00
suchmememanyskill
a84c695a9f Kickstart workflow 2024-01-30 20:39:00 +01:00
suchmememanyskill
c077b6e617 Remove leftover defines 2024-01-30 20:32:45 +01:00
suchmememanyskill
899f89b57d Make ip setup responsive 2024-01-30 20:12:42 +01:00
suchmememanyskill
982c03b0f6 Make wifi-setup responsive 2024-01-30 19:02:59 +01:00
suchmememanyskill
0ba2abd6b1 Rename BIG_GAP to GAP 2024-01-29 20:56:03 +01:00
suchmememanyskill
de1833e219 Make klipper error screen responsive 2024-01-29 20:51:25 +01:00
suchmememanyskill
c5b6401c60 Remove unneeded code 2024-01-29 20:34:25 +01:00
suchmememanyskill
292f879780 Make move panel responsive 2024-01-29 20:32:38 +01:00
suchmememanyskill
c65cc08eb3 Don't break macros 2024-01-28 19:23:35 +01:00
suchmememanyskill
8f29978082 Make temp panel responsive 2024-01-28 17:54:47 +01:00
suchmememanyskill
6750c8f572 Make progress panel responsive 2024-01-28 12:54:18 +01:00
suchmememanyskill
b3c60e4442 Change styling of print panel 2024-01-28 12:14:12 +01:00
suchmememanyskill
4ff96e0278 Make file select responsive 2024-01-28 12:06:35 +01:00
suchmememanyskill
2d6fdb8e84 Set timeout for getting files 2024-01-28 12:06:20 +01:00
suchmememanyskill
66bb1137aa Make generic functions for creating flex parents 2024-01-28 11:40:25 +01:00
suchmememanyskill
db0c335049 Don't show each macro twice 2024-01-28 00:55:33 +01:00
suchmememanyskill
8198729ad3 Rename defines 2024-01-28 00:49:25 +01:00
suchmememanyskill
939a9f6547 Make sidebar ajustable 2024-01-28 00:48:06 +01:00
suchmememanyskill
1c50efa500 Make fonts ajustable 2024-01-28 00:24:33 +01:00
suchmememanyskill
ec75a3e289 Start of porting process 2024-01-28 00:18:52 +01:00
suchmememanyskill
2e252c1d18 Upgrade lvgl 2024-01-26 17:26:41 +01:00
suchmememanyskill
dd20c11d8b Slightly optimise file reader 2024-01-23 20:07:06 +01:00
suchmememanyskill
2a2fff27d6 Merge branch 'master' into dev 2024-01-19 21:10:39 +01:00
suchmememanyskill
082d66ca10 Longer watchdog timeout, refactor, use duty cycle for backlight 2024-01-19 21:05:57 +01:00
Sims
41b4bff940 Merge pull request #12 from suchmememanyskill/dev
Dev
2024-01-15 13:28:57 +01:00
suchmememanyskill
9136f4c94b Add some delay within data loop to give other processes on the core time to process 2024-01-08 21:33:01 +01:00
suchmememanyskill
50f4984231 Insert sort the fetched files, discard any extras 2024-01-07 21:07:35 +01:00
suchmememanyskill
48466cfb44 Lower data fetch task priority 2024-01-07 21:07:19 +01:00
suchmememanyskill
a7acd49d60 Make all colors somewhat worth using 2024-01-06 20:40:37 +01:00
suchmememanyskill
91920a679a Fix #10 2024-01-06 19:59:13 +01:00
suchmememanyskill
53441c86c4 Add kofi to site 2024-01-05 23:20:47 +01:00
Sims
ffdc8ae87e Merge pull request #7 from suchmememanyskill/dev
v1.1.2
2023-12-16 17:53:14 +01:00
suchmememanyskill
7c786d1e6b Don't continously call unfreeze_render_thread() 2023-12-15 19:24:55 +01:00
suchmememanyskill
34c6a5e031 Offload API request loop to core 0 2023-12-15 19:22:48 +01:00
suchmememanyskill
7a430f81c5 Change back to klipper connect screen when connection to klipper gets severed 2023-12-11 22:23:18 +01:00
Sims
230884c2cc Merge pull request #6 from suchmememanyskill/dev
V1.1.1
2023-12-03 01:25:45 +01:00
suchmememanyskill
f2d232d9eb Add visual Klipper connect retry 2023-12-02 02:01:26 +01:00
suchmememanyskill
1e3f0ab637 Lower CPU speed if screen is off 2023-12-02 01:13:40 +01:00
suchmememanyskill
e15c7e37ff update readme 2023-11-23 12:31:32 +01:00
suchmememanyskill
e15ba8d852 Merge branch 'dev' 2023-11-23 12:01:12 +01:00
suchmememanyskill
a759ccbbf7 Merge branch 'master' of https://github.com/suchmememanyskill/CYD-Klipper-Display 2023-11-23 12:01:09 +01:00
suchmememanyskill
84662a8fab Make sure the macro name doesn't overflow 2023-11-23 11:58:10 +01:00
suchmememanyskill
cb47286784 Add macro support 2023-11-23 11:46:09 +01:00
suchmememanyskill
6717e53fc9 Fix filenames with spaces 2023-11-23 10:43:39 +01:00
suchmememanyskill
dc5a3b5efd Add temp presets 2023-11-21 04:01:35 +01:00
Sims
48520f652a Print wifi connect status while connecting, retry ip more times (#3) 2023-11-20 15:57:40 +01:00
suchmememanyskill
dccb10cc6f Print wifi connect status while connecting, retry ip more times 2023-11-14 00:45:08 +01:00
suchmememanyskill
c5d08253a7 Retry IP connect on boot, fix network c string issue 2023-11-14 00:31:52 +01:00
suchmememanyskill
5224e34f8c use absolute coords for position, rather than ajusted ones 2023-11-13 21:55:35 +01:00
122 changed files with 12133 additions and 1535 deletions

View File

@@ -4,22 +4,34 @@ permissions:
pages: write pages: write
id-token: write id-token: write
on: [push, pull_request] on:
push:
branches:
- '*'
tags-ignore:
- '*'
pull_request:
workflow_dispatch:
release:
types: [created]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/cache@v3
with: with:
path: | fetch-depth: 0
~/.cache/pip
~/.platformio/.cache #- uses: actions/cache@v3
key: ${{ runner.os }}-pio # with:
# path: |
# ~/.cache/pip
# ~/.platformio/.cache
# key: ${{ runner.os }}-pio-cyd-klipper
- uses: actions/setup-python@v4 - uses: actions/setup-python@v5
with: with:
python-version: '3.9' python-version: '3.9'
@@ -28,30 +40,16 @@ jobs:
- name: Build PlatformIO Project - name: Build PlatformIO Project
run: | run: |
cd CYD-Klipper python3 ci.py
pio run
- name: Make output dir
run: |
mkdir -p output
- name: Build Binary
run: |
cp ./CYD-Klipper/.pio/build/esp32dev/bootloader.bin output
cp ./CYD-Klipper/.pio/build/esp32dev/partitions.bin output
cp ./CYD-Klipper/.pio/build/esp32dev/firmware.bin output
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin output
python3 -m esptool --chip esp32 merge_bin -o ./output/merged-firmware.bin --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 ./output/bootloader.bin 0x8000 ./output/partitions.bin 0xe000 ./output/boot_app0.bin 0x10000 ./output/firmware.bin
cp -r ./output ./_site
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: firmware name: firmware
path: ./output path: ./out
- name: Upload GitHub Page Artifact - name: Upload GitHub Page Artifact
uses: actions/upload-pages-artifact@v2 uses: actions/upload-pages-artifact@v3
deploy: deploy:
environment: environment:
@@ -59,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 == 'push' && 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: |
@@ -67,5 +65,5 @@ jobs:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v2 uses: actions/deploy-pages@v4

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
_site/out/
_site/OTA.json
_site/esp32-*.json
pyvenv.cfg
bin/
out/
lib
lib64
__pycache__/

0
.gitmodules vendored Normal file
View File

View File

@@ -8,6 +8,47 @@
"unordered_set": "cpp", "unordered_set": "cpp",
"vector": "cpp", "vector": "cpp",
"string_view": "cpp", "string_view": "cpp",
"initializer_list": "cpp" "initializer_list": "cpp",
} "algorithm": "cpp",
"cstddef": "cpp",
"functional": "cpp",
"*.tcc": "cpp",
"cmath": "cpp",
"system_error": "cpp",
"random": "cpp",
"optional": "cpp",
"limits": "cpp",
"memory": "cpp",
"new": "cpp",
"type_traits": "cpp",
"atomic": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdarg": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"exception": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"tuple": "cpp",
"utility": "cpp",
"fstream": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
},
"cmake.configureOnOpen": false
} }

View File

@@ -0,0 +1,3 @@
# Boards
Contains specialised CYD definitions from [platformio-espressif32-sunton](https://github.com/rzeldent/platformio-espressif32-sunton)

View File

@@ -0,0 +1,126 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S022C'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/8)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=0'",
"'-D LCD_ST7789_I80'",
"'-D ST7789_I80_BUS_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7789_I80_BUS_CONFIG_DC=16'",
"'-D ST7789_I80_BUS_CONFIG_WR=4'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D8=15'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D9=13'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D10=12'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D11=14'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D12=27'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D13=25'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D14=33'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D15=32'",
"'-D ST7789_I80_BUS_CONFIG_BUS_WIDTH=8'",
"'-D ST7789_I80_BUS_CONFIG_MAX_TRANSFER_BYTES=(LVGL_BUFFER_PIXELS * sizeof(lv_color_t))'",
"'-D ST7789_I80_BUS_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7789_I80_BUS_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7789_IO_I80_CONFIG_CS_GPIO_NUM=17'",
"'-D ST7789_IO_I80_CONFIG_PCLK_HZ=12000000'",
"'-D ST7789_IO_I80_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7789_IO_I80_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_IDLE_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_CMD_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DUMMY_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DATA_LEVEL=1'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_CS_ACTIVE_HIGH=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_REVERSE_COLOR_BITS=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_SWAP_COLOR_BYTES=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_ACTIVE_NEG=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_IDLE_LOW=0'",
"'-D ST7789_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7789_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_RGB'",
"'-D ST7789_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7789_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D ST7789_RD_GPIO=2'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=false'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_CST816S_I2C'",
"'-D CST816S_I2C_HOST=I2C_NUM_0'",
"'-D CST816S_I2C_CONFIG_SDA_IO_NUM=21'",
"'-D CST816S_I2C_CONFIG_SCL_IO_NUM=22'",
"'-D CST816S_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D CST816S_I2C_CONFIG_CLK_FLAGS=0'",
"'-D CST816S_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS'",
"'-D CST816S_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D CST816S_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D CST816S_IO_I2C_CONFIG_LCD_CMD_BITS=8'",
"'-D CST816S_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D CST816S_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D CST816S_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D CST816S_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_INT_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1",
"-DCYD_SCREEN_DISABLE_INVERT_COLORS=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S022C",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006284154750.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,127 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S022C'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/8)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=0'",
"'-D LCD_ST7789_I80'",
"'-D ST7789_I80_BUS_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7789_I80_BUS_CONFIG_DC=16'",
"'-D ST7789_I80_BUS_CONFIG_WR=4'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D8=15'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D9=13'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D10=12'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D11=14'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D12=27'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D13=25'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D14=33'",
"'-D ST7789_I80_BUS_CONFIG_DATA_GPIO_D15=32'",
"'-D ST7789_I80_BUS_CONFIG_BUS_WIDTH=8'",
"'-D ST7789_I80_BUS_CONFIG_MAX_TRANSFER_BYTES=(LVGL_BUFFER_PIXELS * sizeof(lv_color_t))'",
"'-D ST7789_I80_BUS_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7789_I80_BUS_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7789_IO_I80_CONFIG_CS_GPIO_NUM=17'",
"'-D ST7789_IO_I80_CONFIG_PCLK_HZ=12000000'",
"'-D ST7789_IO_I80_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7789_IO_I80_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_IDLE_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_CMD_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DUMMY_LEVEL=0'",
"'-D ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DATA_LEVEL=1'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_CS_ACTIVE_HIGH=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_REVERSE_COLOR_BITS=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_SWAP_COLOR_BYTES=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_ACTIVE_NEG=0'",
"'-D ST7789_IO_I80_CONFIG_FLAGS_PCLK_IDLE_LOW=0'",
"'-D ST7789_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7789_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_RGB'",
"'-D ST7789_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7789_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D ST7789_RD_GPIO=2'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=false'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_CST816S_I2C'",
"'-D CST816S_I2C_HOST=I2C_NUM_0'",
"'-D CST816S_I2C_CONFIG_SDA_IO_NUM=21'",
"'-D CST816S_I2C_CONFIG_SCL_IO_NUM=22'",
"'-D CST816S_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D CST816S_I2C_CONFIG_CLK_FLAGS=0'",
"'-D CST816S_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS'",
"'-D CST816S_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D CST816S_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D CST816S_IO_I2C_CONFIG_LCD_CMD_BITS=8'",
"'-D CST816S_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D CST816S_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D CST816S_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D CST816S_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_INT_GPIO_NUM=GPIO_NUM_NC'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_VERTICAL=1",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1",
"-DCYD_SCREEN_DISABLE_INVERT_COLORS=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S022C-V",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006284154750.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,122 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S024C'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=27'",
"'-D LCD_ILI9341_SPI'",
"'-D ILI9341_SPI_HOST=SPI2_HOST'",
"'-D ILI9341_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ILI9341_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ILI9341_SPI_BUS_MISO_IO_NUM=12'",
"'-D ILI9341_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ILI9341_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ILI9341_SPI_BUS_FLAGS=0'",
"'-D ILI9341_SPI_BUS_INTR_FLAGS=0'",
"'-D ILI9341_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ILI9341_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ILI9341_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D ILI9341_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ILI9341_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ILI9341_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ILI9341_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ILI9341_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_BGR'",
"'-D ILI9341_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ILI9341_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ILI9341_DEV_CONFIG_VENDOR_CONFIG=\"(ili9341_vendor_config_t[]){{.init_cmds=(ili9341_lcd_init_cmd_t[]){{.cmd=0xCF,.data=(uint8_t[]){0x00,0xC1,0x30},.data_bytes=3},{.cmd=0xED,.data=(uint8_t[]){0x64,0x03,0x12,0x81},.data_bytes=4},{.cmd=0xE8,.data=(uint8_t[]){0x85,0x00,0x78},.data_bytes=3},{.cmd=0xCB,.data=(uint8_t[]){0x39,0x2C,0x00,0x34,0x02},.data_bytes=5},{.cmd=0xF7,.data=(uint8_t[]){0x20},.data_bytes=1},{.cmd=0xEA,.data=(uint8_t[]){0x00,0x00},.data_bytes=2},{.cmd=0xC0,.data=(uint8_t[]){0x10},.data_bytes=1},{.cmd=0xC1,.data=(uint8_t[]){0x00},.data_bytes=1},{.cmd=0xC5,.data=(uint8_t[]){0x30,0x30},.data_bytes=2,},{.cmd=0xC7,.data=(uint8_t[]){0xB7},.data_bytes=1},{.cmd=0x3A,.data=(uint8_t[]){0x55},.data_bytes=1},{.cmd=0x36,.data=(uint8_t[]){0x08},.data_bytes=1},{.cmd=0xB1,.data=(uint8_t[]){0x00,0x1A},.data_bytes=2},{.cmd=0xB6,.data=(uint8_t[]){0x08,0x82,0x27},.data_bytes=3},{.cmd=0xF2,.data=(uint8_t[]){0x00},.data_bytes=1},{.cmd=0x26,.data=(uint8_t[]){0x01},.data_bytes=1},{.cmd=0xE0,.data=(uint8_t[]){0x0F,0x2A,0x28,0x08,0x0E,0x08,0x54,0xA9,0x43,0x0A,0x0F,0x00,0x00,0x00,0x00},.data_bytes=15},{.cmd=0xE1,.data=(uint8_t[]){0x00,0x15,0x17,0x07,0x11,0x06,0x2B,0x56,0x3C,0x05,0x10,0x0F,0x3F,0x3F,0x0F},.data_bytes=15},{.cmd=0x2B,.data=(uint8_t[]){0x00,0x00,0x01,0x3F},.data_bytes=4},{.cmd=0x2A,.data=(uint8_t[]){0x00,0x00,0x00,0xEF},.data_bytes=4},{.cmd=0x21},{.cmd=0x11,.delay_ms=120},{.cmd=0x29,.delay_ms=1}},.init_cmds_size=23}}\"'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=true'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_CST816S_I2C'",
"'-D CST816S_I2C_HOST=I2C_NUM_0'",
"'-D CST816S_I2C_CONFIG_SDA_IO_NUM=33'",
"'-D CST816S_I2C_CONFIG_SCL_IO_NUM=32'",
"'-D CST816S_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_ENABLE'",
"'-D CST816S_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D CST816S_I2C_CONFIG_CLK_FLAGS=0'",
"'-D CST816S_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS'",
"'-D CST816S_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D CST816S_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D CST816S_IO_I2C_CONFIG_LCD_CMD_BITS=8'",
"'-D CST816S_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D CST816S_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D CST816S_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D CST816S_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D CST816S_TOUCH_CONFIG_RST_GPIO_NUM=25'",
"'-D CST816S_TOUCH_CONFIG_INT_GPIO_NUM=21'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D CST816S_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S024C-SD",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005005865107357.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,63 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"-DUSER_SETUP_LOADED=1",
"-DILI9341_2_DRIVER=1",
"-DTFT_BACKLIGHT_ON=HIGH",
"-DTFT_BL=21",
"-DTFT_MISO=12",
"-DTFT_MOSI=13",
"-DTFT_SCLK=14",
"-DTFT_CS=15",
"-DTFT_DC=2",
"-DTFT_RST=-1",
"-DLOAD_GCLD=1",
"-DSPI_FREQUENCY=55000000",
"-DSPI_READ_FREQUENCY=20000000",
"-DSPI_TOUCH_FREQUENCY=2500000",
"-DTOUCH_CS=-1",
"-DCYD_SCREEN_HEIGHT_PX=240",
"-DCYD_SCREEN_WIDTH_PX=320",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_2432S028R=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S028R",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004502250619.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,125 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S028R'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=21'",
"'-D LCD_ILI9341_SPI'",
"'-D ILI9341_SPI_HOST=SPI2_HOST'",
"'-D ILI9341_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ILI9341_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ILI9341_SPI_BUS_MISO_IO_NUM=12'",
"'-D ILI9341_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ILI9341_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ILI9341_SPI_BUS_FLAGS=0'",
"'-D ILI9341_SPI_BUS_INTR_FLAGS=0'",
"'-D ILI9341_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ILI9341_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ILI9341_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D ILI9341_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ILI9341_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ILI9341_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ILI9341_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ILI9341_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_BGR'",
"'-D ILI9341_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ILI9341_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ILI9341_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=true'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_XPT2046_SPI'",
"'-D XPT2046_SPI_HOST=SPI3_HOST'",
"'-D XPT2046_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D XPT2046_SPI_BUS_MOSI_IO_NUM=32'",
"'-D XPT2046_SPI_BUS_MISO_IO_NUM=39'",
"'-D XPT2046_SPI_BUS_SCLK_IO_NUM=25'",
"'-D XPT2046_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_CS_GPIO_NUM=33'",
"'-D XPT2046_SPI_CONFIG_DC_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D XPT2046_SPI_CONFIG_PCLK_HZ=2000000'",
"'-D XPT2046_SPI_CONFIG_TRANS_QUEUE_DEPTH=3'",
"'-D XPT2046_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D XPT2046_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D XPT2046_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D XPT2046_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D XPT2046_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_TOUCH_CONFIG_INT_GPIO_NUM=36'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=true'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S028Rv1-SD",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004502250619.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,125 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S028Rv2'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=21'",
"'-D LCD_ILI9341_SPI'",
"'-D ILI9341_SPI_HOST=SPI2_HOST'",
"'-D ILI9341_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ILI9341_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ILI9341_SPI_BUS_MISO_IO_NUM=12'",
"'-D ILI9341_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ILI9341_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ILI9341_SPI_BUS_FLAGS=0'",
"'-D ILI9341_SPI_BUS_INTR_FLAGS=0'",
"'-D ILI9341_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ILI9341_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ILI9341_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D ILI9341_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ILI9341_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ILI9341_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ILI9341_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ILI9341_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ILI9341_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ILI9341_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_BGR'",
"'-D ILI9341_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ILI9341_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ILI9341_DEV_CONFIG_VENDOR_CONFIG=\"(ili9341_vendor_config_t[]){{.init_cmds=(ili9341_lcd_init_cmd_t[]){{.cmd=0xCF,.data=(uint8_t[]){0x00,0xC1,0x30},.data_bytes=3},{.cmd=0xED,.data=(uint8_t[]){0x64,0x03,0x12,0x81},.data_bytes=4},{.cmd=0xE8,.data=(uint8_t[]){0x85,0x00,0x78},.data_bytes=3},{.cmd=0xCB,.data=(uint8_t[]){0x39,0x2C,0x00,0x34,0x02},.data_bytes=5},{.cmd=0xF7,.data=(uint8_t[]){0x20},.data_bytes=1},{.cmd=0xEA,.data=(uint8_t[]){0x00,0x00},.data_bytes=2},{.cmd=0xC0,.data=(uint8_t[]){0x10},.data_bytes=1},{.cmd=0xC1,.data=(uint8_t[]){0x00},.data_bytes=1},{.cmd=0xC5,.data=(uint8_t[]){0x30,0x30},.data_bytes=2,},{.cmd=0xC7,.data=(uint8_t[]){0xB7},.data_bytes=1},{.cmd=0x3A,.data=(uint8_t[]){0x55},.data_bytes=1},{.cmd=0x36,.data=(uint8_t[]){0x08},.data_bytes=1},{.cmd=0xB1,.data=(uint8_t[]){0x00,0x1A},.data_bytes=2},{.cmd=0xB6,.data=(uint8_t[]){0x08,0x82,0x27},.data_bytes=3},{.cmd=0xF2,.data=(uint8_t[]){0x00},.data_bytes=1},{.cmd=0x26,.data=(uint8_t[]){0x01},.data_bytes=1},{.cmd=0xE0,.data=(uint8_t[]){0x0F,0x2A,0x28,0x08,0x0E,0x08,0x54,0xA9,0x43,0x0A,0x0F,0x00,0x00,0x00,0x00},.data_bytes=15},{.cmd=0xE1,.data=(uint8_t[]){0x00,0x15,0x17,0x07,0x11,0x06,0x2B,0x56,0x3C,0x05,0x10,0x0F,0x3F,0x3F,0x0F},.data_bytes=15},{.cmd=0x2B,.data=(uint8_t[]){0x00,0x00,0x01,0x3F},.data_bytes=4},{.cmd=0x2A,.data=(uint8_t[]){0x00,0x00,0x00,0xEF},.data_bytes=4},{.cmd=0x21},{.cmd=0x11,.delay_ms=120},{.cmd=0x29,.delay_ms=1}},.init_cmds_size=23}}\"'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=true'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_XPT2046_SPI'",
"'-D XPT2046_SPI_HOST=SPI3_HOST'",
"'-D XPT2046_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D XPT2046_SPI_BUS_MOSI_IO_NUM=32'",
"'-D XPT2046_SPI_BUS_MISO_IO_NUM=39'",
"'-D XPT2046_SPI_BUS_SCLK_IO_NUM=25'",
"'-D XPT2046_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_CS_GPIO_NUM=33'",
"'-D XPT2046_SPI_CONFIG_DC_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D XPT2046_SPI_CONFIG_PCLK_HZ=2000000'",
"'-D XPT2046_SPI_CONFIG_TRANS_QUEUE_DEPTH=3'",
"'-D XPT2046_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D XPT2046_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D XPT2046_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D XPT2046_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D XPT2046_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_TOUCH_CONFIG_INT_GPIO_NUM=36'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=true'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S028Rv2-SD",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004502250619.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,125 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S028Rv3'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=21'",
"'-D LCD_ST7789_SPI'",
"'-D ST7789_SPI_HOST=SPI2_HOST'",
"'-D ST7789_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ST7789_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ST7789_SPI_BUS_MISO_IO_NUM=GPIO_NUM_NC'",
"'-D ST7789_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ST7789_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ST7789_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ST7789_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ST7789_SPI_BUS_FLAGS=0'",
"'-D ST7789_SPI_BUS_INTR_FLAGS=0'",
"'-D ST7789_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ST7789_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ST7789_SPI_CONFIG_SPI_MODE=SPI_MODE3'",
"'-D ST7789_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ST7789_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7789_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7789_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7789_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ST7789_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7789_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_RGB'",
"'-D ST7789_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7789_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=false'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_XPT2046_SPI'",
"'-D XPT2046_SPI_HOST=SPI3_HOST'",
"'-D XPT2046_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D XPT2046_SPI_BUS_MOSI_IO_NUM=32'",
"'-D XPT2046_SPI_BUS_MISO_IO_NUM=39'",
"'-D XPT2046_SPI_BUS_SCLK_IO_NUM=25'",
"'-D XPT2046_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_CS_GPIO_NUM=33'",
"'-D XPT2046_SPI_CONFIG_DC_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D XPT2046_SPI_CONFIG_PCLK_HZ=2000000'",
"'-D XPT2046_SPI_CONFIG_TRANS_QUEUE_DEPTH=3'",
"'-D XPT2046_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D XPT2046_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D XPT2046_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D XPT2046_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D XPT2046_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_TOUCH_CONFIG_INT_GPIO_NUM=36'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=true'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S028Rv3-SD",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004502250619.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,125 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_2432S032C'",
"'-D LCD_WIDTH=240'",
"'-D LCD_HEIGHT=320'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=27'",
"'-D LCD_IPS'",
"'-D LCD_ST7789_SPI'",
"'-D ST7789_SPI_HOST=SPI2_HOST'",
"'-D ST7789_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ST7789_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ST7789_SPI_BUS_MISO_IO_NUM=12'",
"'-D ST7789_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ST7789_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ST7789_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ST7789_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ST7789_SPI_BUS_FLAGS=0'",
"'-D ST7789_SPI_BUS_INTR_FLAGS=0'",
"'-D ST7789_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ST7789_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ST7789_SPI_CONFIG_SPI_MODE=SPI_MODE3'",
"'-D ST7789_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ST7789_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7789_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7789_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7789_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ST7789_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ST7789_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7789_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_RGB'",
"'-D ST7789_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7789_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=false'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_GT911_I2C'",
"'-D GT911_I2C_HOST=I2C_NUM_0'",
"'-D GT911_I2C_CONFIG_SDA_IO_NUM=33'",
"'-D GT911_I2C_CONFIG_SCL_IO_NUM=32'",
"'-D GT911_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D GT911_I2C_CONFIG_CLK_FLAGS=I2C_SCLK_SRC_FLAG_FOR_NOMAL'",
"'-D GT911_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS'",
"'-D GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D GT911_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D GT911_IO_I2C_CONFIG_LCD_CMD_BITS=16'",
"'-D GT911_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D GT911_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D GT911_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D GT911_TOUCH_CONFIG_RST_GPIO_NUM=25'",
"'-D GT911_TOUCH_CONFIG_INT_GPIO_NUM=21'",
"'-D GT911_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D GT911_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_HEIGHT_PX=240",
"-DCYD_SCREEN_WIDTH_PX=320",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-2432S032C-SD",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006224494145.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,122 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32_DEV'",
"'-D ESP32_3248S035C'",
"'-D LCD_WIDTH=320'",
"'-D LCD_HEIGHT=480'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT/4)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=27'",
"'-D LCD_ST7796_SPI'",
"'-D ST7796_SPI_HOST=SPI2_HOST'",
"'-D ST7796_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D ST7796_SPI_BUS_MOSI_IO_NUM=13'",
"'-D ST7796_SPI_BUS_MISO_IO_NUM=12'",
"'-D ST7796_SPI_BUS_SCLK_IO_NUM=14'",
"'-D ST7796_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D ST7796_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D ST7796_SPI_BUS_MAX_TRANSFER_SZ=(LVGL_BUFFER_PIXELS*sizeof(lv_color16_t))'",
"'-D ST7796_SPI_BUS_FLAGS=0'",
"'-D ST7796_SPI_BUS_INTR_FLAGS=0'",
"'-D ST7796_SPI_CONFIG_CS_GPIO_NUM=15'",
"'-D ST7796_SPI_CONFIG_DC_GPIO_NUM=2'",
"'-D ST7796_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D ST7796_SPI_CONFIG_PCLK_HZ=24000000'",
"'-D ST7796_SPI_CONFIG_TRANS_QUEUE_DEPTH=10'",
"'-D ST7796_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D ST7796_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D ST7796_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D ST7796_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D ST7796_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D ST7796_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D ST7796_DEV_CONFIG_RESET_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7796_DEV_CONFIG_COLOR_SPACE=ESP_LCD_COLOR_SPACE_BGR'",
"'-D ST7796_DEV_CONFIG_BITS_PER_PIXEL=16'",
"'-D ST7796_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH=false'",
"'-D ST7796_DEV_CONFIG_VENDOR_CONFIG=NULL'",
"'-D LCD_SWAP_XY=false'",
"'-D LCD_MIRROR_X=true'",
"'-D LCD_MIRROR_Y=false'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_GT911_I2C'",
"'-D GT911_I2C_HOST=I2C_NUM_0'",
"'-D GT911_I2C_CONFIG_SDA_IO_NUM=33'",
"'-D GT911_I2C_CONFIG_SCL_IO_NUM=32'",
"'-D GT911_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D GT911_I2C_CONFIG_CLK_FLAGS=I2C_SCLK_SRC_FLAG_FOR_NOMAL'",
"'-D GT911_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS'",
"'-D GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D GT911_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D GT911_IO_I2C_CONFIG_LCD_CMD_BITS=16'",
"'-D GT911_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D GT911_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D GT911_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D GT911_TOUCH_CONFIG_RST_GPIO_NUM=25'",
"'-D GT911_TOUCH_CONFIG_INT_GPIO_NUM=21'",
"'-D GT911_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D GT911_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=true'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=5'",
"'-D TF_SPI_MOSI=23'",
"'-D TF_SPI_SCLK=18'",
"'-D TF_SPI_MISO=19'",
"'-D BOARD_HAS_RGB_LED'",
"'-D RGB_LED_R=4'",
"'-D RGB_LED_G=16'",
"'-D RGB_LED_B=17'",
"'-D BOARD_HAS_CDS'",
"'-D CDS=34'",
"'-D BOARD_HAS_SPEAK'",
"'-D SPEAK=26'",
"-DCYD_SCREEN_GAP_PX=10",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=45",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=45",
"-DCYD_SCREEN_FONT=lv_font_montserrat_16",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=50",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-3248S035C",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004632953455.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,66 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"-DUSER_SETUP_LOADED=1",
"-DST7796_DRIVER=1",
"-DTFT_BL=27",
"-DTFT_BACKLIGHT_ON=HIGH",
"-DTFT_MISO=12",
"-DTFT_MOSI=13",
"-DTFT_SCLK=14",
"-DTFT_CS=15",
"-DTFT_DC=2",
"-DTFT_RST=-1",
"-DLOAD_GCLD=1",
"-DSPI_FREQUENCY=80000000",
"-DSPI_READ_FREQUENCY=20000000",
"-DSPI_TOUCH_FREQUENCY=2500000",
"-DTOUCH_CS=-1",
"-DCYD_SCREEN_HEIGHT_PX=480",
"-DCYD_SCREEN_WIDTH_PX=320",
"-DCYD_SCREEN_GAP_PX=10",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=45",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=45",
"-DCYD_SCREEN_FONT=lv_font_montserrat_16",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=50",
"-DCYD_SCREEN_DRIVER_ESP32_3248S035C=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1",
"-DCYD_SCREEN_VERTICAL=1",
"-DCYD_SCREEN_NO_TEMP_SCROLL=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-3248S035C-V",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004632953455.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,64 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"-DUSER_SETUP_LOADED=1",
"-DST7796_DRIVER=1",
"-DTFT_BL=27",
"-DTFT_BACKLIGHT_ON=HIGH",
"-DTFT_MISO=12",
"-DTFT_MOSI=13",
"-DTFT_SCLK=14",
"-DTFT_CS=15",
"-DTFT_DC=2",
"-DTFT_RST=-1",
"-DLOAD_GCLD=1",
"-DSPI_FREQUENCY=80000000",
"-DSPI_READ_FREQUENCY=20000000",
"-DSPI_TOUCH_FREQUENCY=2500000",
"-DTOUCH_CS=-1",
"-DCYD_SCREEN_HEIGHT_PX=320",
"-DCYD_SCREEN_WIDTH_PX=480",
"-DCYD_SCREEN_GAP_PX=10",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=45",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=45",
"-DCYD_SCREEN_FONT=lv_font_montserrat_16",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=50",
"-DCYD_SCREEN_DRIVER_ESP32_3248S035C=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-3248S035C",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004632953455.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,143 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32S3_DEV'",
"'-D BOARD_HAS_PSRAM'",
"'-D ARDUINO_USB_MODE=1'",
"'-D ARDUINO_RUNNING_CORE=1'",
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
"'-D ARDUINO_USB_CDC_ON_BOOT=0'",
"'-D ESP32_4827S043C'",
"'-D LCD_WIDTH=480'",
"'-D LCD_HEIGHT=272'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=2'",
"'-D LCD_ST7262_PAR'",
"'-D ST7262_PANEL_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ=(8*1000000)'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_H_RES=LCD_WIDTH'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_V_RES=LCD_HEIGHT'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH=43'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH=12'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_DATA_WIDTH=16'",
"'-D ST7262_PANEL_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7262_PANEL_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7262_PANEL_CONFIG_HSYNC_GPIO_NUM=39'",
"'-D ST7262_PANEL_CONFIG_VSYNC_GPIO_NUM=41'",
"'-D ST7262_PANEL_CONFIG_DE_GPIO_NUM=40'",
"'-D ST7262_PANEL_CONFIG_PCLK_GPIO_NUM=42'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R0=8'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R1=3'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R2=46'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R3=9'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R4=1'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G0=5'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G1=6'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G2=7'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G3=15'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G4=16'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G5=4'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B0=45'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B1=48'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B2=47'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B3=21'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B4=14'",
"'-D ST7262_PANEL_CONFIG_DISP_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7262_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_FB_IN_PSRAM=true'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_GT911_I2C'",
"'-D GT911_I2C_HOST=I2C_NUM_0'",
"'-D GT911_I2C_CONFIG_SDA_IO_NUM=19'",
"'-D GT911_I2C_CONFIG_SCL_IO_NUM=20'",
"'-D GT911_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D GT911_I2C_CONFIG_CLK_FLAGS=I2C_SCLK_SRC_FLAG_FOR_NOMAL'",
"'-D GT911_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS'",
"'-D GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D GT911_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D GT911_IO_I2C_CONFIG_LCD_CMD_BITS=16'",
"'-D GT911_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D GT911_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D GT911_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D GT911_TOUCH_CONFIG_RST_GPIO_NUM=38'",
"'-D GT911_TOUCH_CONFIG_INT_GPIO_NUM=18'",
"'-D GT911_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D GT911_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=10'",
"'-D TF_SPI_MOSI=11'",
"'-D TF_SPI_SCLK=12'",
"'-D TF_SPI_MISO=13'",
"'-DCYD_SCREEN_HEIGHT_PX=272'",
"'-DCYD_SCREEN_WIDTH_PX=480'",
"-DROTATION_INVERTED=LV_DISP_ROT_180",
"-DROTATION_NORMAL=LV_DISP_ROT_NONE",
"'-DCYD_SCREEN_GAP_PX=10'",
"'-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35'",
"'-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=40'",
"'-DCYD_SCREEN_FONT=lv_font_montserrat_16'",
"'-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12'",
"'-DCYD_SCREEN_SIDEBAR_SIZE_PX=50'",
"'-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1'",
"'-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1'"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-4827S043C-SD",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004788147691.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,146 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32S3_DEV'",
"'-D BOARD_HAS_PSRAM'",
"'-D ARDUINO_USB_MODE=1'",
"'-D ARDUINO_RUNNING_CORE=1'",
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
"'-D ARDUINO_USB_CDC_ON_BOOT=0'",
"'-D ESP32_4827S043R'",
"'-D LCD_WIDTH=480'",
"'-D LCD_HEIGHT=272'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=2'",
"'-D LCD_ST7262_PAR'",
"'-D ST7262_PANEL_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ=(8*1000000)'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_H_RES=LCD_WIDTH'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_V_RES=LCD_HEIGHT'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH=43'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH=12'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_DATA_WIDTH=16'",
"'-D ST7262_PANEL_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7262_PANEL_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7262_PANEL_CONFIG_HSYNC_GPIO_NUM=39'",
"'-D ST7262_PANEL_CONFIG_VSYNC_GPIO_NUM=41'",
"'-D ST7262_PANEL_CONFIG_DE_GPIO_NUM=40'",
"'-D ST7262_PANEL_CONFIG_PCLK_GPIO_NUM=42'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R0=8'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R1=3'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R2=46'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R3=9'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R4=1'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G0=5'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G1=6'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G2=7'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G3=15'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G4=16'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G5=4'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B0=45'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B1=48'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B2=47'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B3=21'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B4=14'",
"'-D ST7262_PANEL_CONFIG_DISP_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7262_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_FB_IN_PSRAM=true'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_XPT2046_SPI'",
"'-D XPT2046_SPI_HOST=SPI2_HOST'",
"'-D XPT2046_SPI_DMA_CHANNEL=SPI_DMA_CH_AUTO'",
"'-D XPT2046_SPI_BUS_MOSI_IO_NUM=11'",
"'-D XPT2046_SPI_BUS_MISO_IO_NUM=13'",
"'-D XPT2046_SPI_BUS_SCLK_IO_NUM=12'",
"'-D XPT2046_SPI_BUS_QUADWP_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_BUS_QUADHD_IO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_CS_GPIO_NUM=38'",
"'-D XPT2046_SPI_CONFIG_DC_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_SPI_CONFIG_SPI_MODE=SPI_MODE0'",
"'-D XPT2046_SPI_CONFIG_PCLK_HZ=2000000'",
"'-D XPT2046_SPI_CONFIG_TRANS_QUEUE_DEPTH=3'",
"'-D XPT2046_SPI_CONFIG_LCD_CMD_BITS=8'",
"'-D XPT2046_SPI_CONFIG_LCD_PARAM_BITS=8'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_AS_CMD_PHASE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_OCTAL_MODE=false'",
"'-D XPT2046_SPI_CONFIG_FLAGS_LSB_FIRST=false'",
"'-D XPT2046_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D XPT2046_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D XPT2046_TOUCH_CONFIG_RST_GPIO_NUM=GPIO_NUM_NC'",
"'-D XPT2046_TOUCH_CONFIG_INT_GPIO_NUM=18'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D XPT2046_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=10'",
"'-D TF_SPI_MOSI=11'",
"'-D TF_SPI_SCLK=12'",
"'-D TF_SPI_MISO=13'",
"'-DCYD_SCREEN_HEIGHT_PX=272'",
"'-DCYD_SCREEN_WIDTH_PX=480'",
"-DROTATION_INVERTED=LV_DISP_ROT_180",
"-DROTATION_NORMAL=LV_DISP_ROT_NONE",
"'-DCYD_SCREEN_GAP_PX=10'",
"'-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35'",
"'-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=40'",
"'-DCYD_SCREEN_FONT=lv_font_montserrat_16'",
"'-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12'",
"'-DCYD_SCREEN_SIDEBAR_SIZE_PX=50'",
"'-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1'"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-4827S043R-SD",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004788147691.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,143 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32S3_DEV'",
"'-D BOARD_HAS_PSRAM'",
"'-D ARDUINO_USB_MODE=1'",
"'-D ARDUINO_RUNNING_CORE=1'",
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
"'-D ARDUINO_USB_CDC_ON_BOOT=0'",
"'-D ESP32_8048S043C'",
"'-D LCD_WIDTH=800'",
"'-D LCD_HEIGHT=480'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=2'",
"'-D LCD_ST7262_PAR'",
"'-D ST7262_PANEL_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ=14000000'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_H_RES=LCD_WIDTH'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_V_RES=LCD_HEIGHT'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_DATA_WIDTH=16'",
"'-D ST7262_PANEL_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7262_PANEL_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7262_PANEL_CONFIG_HSYNC_GPIO_NUM=39'",
"'-D ST7262_PANEL_CONFIG_VSYNC_GPIO_NUM=41'",
"'-D ST7262_PANEL_CONFIG_DE_GPIO_NUM=40'",
"'-D ST7262_PANEL_CONFIG_PCLK_GPIO_NUM=42'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R0=8'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R1=3'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R2=46'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R3=9'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R4=1'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G0=5'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G1=6'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G2=7'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G3=15'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G4=16'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G5=4'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B0=45'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B1=48'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B2=47'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B3=21'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B4=14'",
"'-D ST7262_PANEL_CONFIG_DISP_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7262_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_FB_IN_PSRAM=true'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_GT911_I2C'",
"'-D GT911_I2C_HOST=I2C_NUM_0'",
"'-D GT911_I2C_CONFIG_SDA_IO_NUM=19'",
"'-D GT911_I2C_CONFIG_SCL_IO_NUM=20'",
"'-D GT911_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D GT911_I2C_CONFIG_CLK_FLAGS=I2C_SCLK_SRC_FLAG_FOR_NOMAL'",
"'-D GT911_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS'",
"'-D GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D GT911_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D GT911_IO_I2C_CONFIG_LCD_CMD_BITS=16'",
"'-D GT911_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D GT911_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D GT911_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D GT911_TOUCH_CONFIG_RST_GPIO_NUM=38'",
"'-D GT911_TOUCH_CONFIG_INT_GPIO_NUM=18'",
"'-D GT911_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D GT911_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=10'",
"'-D TF_SPI_MOSI=11'",
"'-D TF_SPI_SCLK=12'",
"'-D TF_SPI_MISO=13'",
"-DCYD_SCREEN_HEIGHT_PX=480",
"-DCYD_SCREEN_WIDTH_PX=800",
"-DROTATION_INVERTED=LV_DISP_ROT_180",
"-DROTATION_NORMAL=LV_DISP_ROT_NONE",
"-DCYD_SCREEN_GAP_PX=15",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=60",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=60",
"-DCYD_SCREEN_FONT=lv_font_montserrat_22",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_16",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=70",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-8048S043C-SD-alt",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006110360174.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,143 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"'-D ARDUINO_ESP32S3_DEV'",
"'-D BOARD_HAS_PSRAM'",
"'-D ARDUINO_USB_MODE=1'",
"'-D ARDUINO_RUNNING_CORE=1'",
"'-D ARDUINO_EVENT_RUNNING_CORE=1'",
"'-D ARDUINO_USB_CDC_ON_BOOT=0'",
"'-D ESP32_8048S043C'",
"'-D LCD_WIDTH=800'",
"'-D LCD_HEIGHT=480'",
"'-D LVGL_BUFFER_PIXELS=(LCD_WIDTH*LCD_HEIGHT)'",
"'-D LVGL_BUFFER_MALLOC_FLAGS=(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT)'",
"'-D GPIO_BCKL=2'",
"'-D LCD_ST7262_PAR'",
"'-D ST7262_PANEL_CONFIG_CLK_SRC=LCD_CLK_SRC_PLL160M'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ=(12.5*1000000)'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_H_RES=LCD_WIDTH'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_V_RES=LCD_HEIGHT'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH=4'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH=8'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG=true'",
"'-D ST7262_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH=false'",
"'-D ST7262_PANEL_CONFIG_DATA_WIDTH=16'",
"'-D ST7262_PANEL_CONFIG_SRAM_TRANS_ALIGN=4'",
"'-D ST7262_PANEL_CONFIG_PSRAM_TRANS_ALIGN=64'",
"'-D ST7262_PANEL_CONFIG_HSYNC_GPIO_NUM=39'",
"'-D ST7262_PANEL_CONFIG_VSYNC_GPIO_NUM=41'",
"'-D ST7262_PANEL_CONFIG_DE_GPIO_NUM=40'",
"'-D ST7262_PANEL_CONFIG_PCLK_GPIO_NUM=42'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R0=8'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R1=3'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R2=46'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R3=9'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_R4=1'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G0=5'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G1=6'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G2=7'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G3=15'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G4=16'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_G5=4'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B0=45'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B1=48'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B2=47'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B3=21'",
"'-D ST7262_PANEL_CONFIG_DATA_GPIO_B4=14'",
"'-D ST7262_PANEL_CONFIG_DISP_GPIO_NUM=GPIO_NUM_NC'",
"'-D ST7262_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE=false'",
"'-D ST7262_PANEL_CONFIG_FLAGS_FB_IN_PSRAM=true'",
"'-D BOARD_HAS_TOUCH'",
"'-D TOUCH_GT911_I2C'",
"'-D GT911_I2C_HOST=I2C_NUM_0'",
"'-D GT911_I2C_CONFIG_SDA_IO_NUM=19'",
"'-D GT911_I2C_CONFIG_SCL_IO_NUM=20'",
"'-D GT911_I2C_CONFIG_SDA_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_SCL_PULLUP_EN=GPIO_PULLUP_DISABLE'",
"'-D GT911_I2C_CONFIG_MASTER_CLK_SPEED=400000'",
"'-D GT911_I2C_CONFIG_CLK_FLAGS=I2C_SCLK_SRC_FLAG_FOR_NOMAL'",
"'-D GT911_IO_I2C_CONFIG_DEV_ADDR=ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS'",
"'-D GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES=1'",
"'-D GT911_IO_I2C_CONFIG_DC_BIT_OFFSET=0'",
"'-D GT911_IO_I2C_CONFIG_LCD_CMD_BITS=16'",
"'-D GT911_IO_I2C_CONFIG_LCD_PARAM_BITS=0'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA=false'",
"'-D GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE=true'",
"'-D GT911_TOUCH_CONFIG_X_MAX=LCD_WIDTH'",
"'-D GT911_TOUCH_CONFIG_Y_MAX=LCD_HEIGHT'",
"'-D GT911_TOUCH_CONFIG_RST_GPIO_NUM=38'",
"'-D GT911_TOUCH_CONFIG_INT_GPIO_NUM=18'",
"'-D GT911_TOUCH_CONFIG_LEVELS_RESET=0'",
"'-D GT911_TOUCH_CONFIG_LEVELS_INTERRUPT=0'",
"'-D TOUCH_SWAP_XY=false'",
"'-D TOUCH_SWAP_X=false'",
"'-D TOUCH_SWAP_Y=false'",
"'-D BOARD_HAS_TF'",
"'-D TF_CS=10'",
"'-D TF_SPI_MOSI=11'",
"'-D TF_SPI_SCLK=12'",
"'-D TF_SPI_MISO=13'",
"-DCYD_SCREEN_HEIGHT_PX=480",
"-DCYD_SCREEN_WIDTH_PX=800",
"-DROTATION_INVERTED=LV_DISP_ROT_180",
"-DROTATION_NORMAL=LV_DISP_ROT_NONE",
"-DCYD_SCREEN_GAP_PX=15",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=60",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=60",
"-DCYD_SCREEN_FONT=lv_font_montserrat_22",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_16",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=70",
"-DCYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-8048S043C-SD",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005006110360174.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,63 @@
{
"build": {
"arduino": {
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": [
"-DUSER_SETUP_LOADED=1",
"-DILI9341_2_DRIVER=1",
"-DTFT_BACKLIGHT_ON=HIGH",
"-DTFT_BL=27",
"-DTFT_MISO=12",
"-DTFT_MOSI=13",
"-DTFT_SCLK=14",
"-DTFT_CS=15",
"-DTFT_DC=2",
"-DTFT_RST=-1",
"-DLOAD_GCLD=1",
"-DSPI_FREQUENCY=15999999",
"-DSPI_READ_FREQUENCY=20000000",
"-DSPI_TOUCH_FREQUENCY=600000",
"-DTOUCH_CS=33",
"-DCYD_SCREEN_HEIGHT_PX=240",
"-DCYD_SCREEN_WIDTH_PX=320",
"-DCYD_SCREEN_GAP_PX=8",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=35",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=35",
"-DCYD_SCREEN_FONT=lv_font_montserrat_14",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_10",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=40",
"-DCYD_SCREEN_DRIVER_ESP32_CROWPANEL_28R=1"
],
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-crowpanel-28R",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.aliexpress.com/item/1005004502250619.html",
"vendor": "Sunton"
}

View File

@@ -0,0 +1,55 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DCYD_SCREEN_HEIGHT_PX=320",
"-DCYD_SCREEN_WIDTH_PX=480",
"-DCYD_SCREEN_GAP_PX=10",
"-DCYD_SCREEN_MIN_BUTTON_HEIGHT_PX=45",
"-DCYD_SCREEN_MIN_BUTTON_WIDTH_PX=45",
"-DCYD_SCREEN_FONT=lv_font_montserrat_16",
"-DCYD_SCREEN_FONT_SMALL=lv_font_montserrat_12",
"-DCYD_SCREEN_SIDEBAR_SIZE_PX=50",
"-DCYD_SCREEN_DRIVER_ESP32_CROWPANEL_35C=1",
"-DCYD_SCREEN_DISABLE_TOUCH_CALIBRATION=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "esp32-8048S043C-SD",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.elecrow.com/esp-terminal-with-esp32-3-5-inch-parallel-480x320-tft-capacitive-touch-display-rgb-by-chip-ili9488.html",
"vendor": "CROWPANEL"
}

View File

@@ -0,0 +1,31 @@
import subprocess
def extract_commit() -> str:
git_describe_output = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, text=True, check=True).stdout.strip()
split_output = git_describe_output.split("-")
if (len(split_output) >= 3):
return f"{split_output[0]}\\ ({split_output[2][1:]})"
else:
return split_output[0]
try:
version = extract_commit()
except:
version = "Unknown"
flag = "-D REPO_VERSION=\\\"" + version + "\\\""
dev_flag = "-DREPO_DEVELOPMENT=1"
print(f"Version: {version}")
print(f"Flag: {flag}")
flags = [flag]
if ('(' in version):
flags.append(dev_flag)
Import("env")
env.Append(
BUILD_FLAGS=flags
)

View File

@@ -8,35 +8,112 @@
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[env:esp32dev] [env]
platform = espressif32 platform = espressif32@6.4.0
board = esp32dev board = esp32dev
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
debug_build_flags = -Os
lib_deps = lib_deps =
SPI https://github.com/suchmememanyskill/esp32-smartdisplay#9c1d737
lvgl/lvgl@^8.3.9 bblanchon/ArduinoJson@^7.0.0
https://github.com/Bodmer/TFT_eSPI.git plageoj/UrlEncode@^1.0.1
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git knolleary/PubSubClient@^2.8
bblanchon/ArduinoJson@^6.21.3 WiFiClientSecure
monitor_filters = esp32_exception_decoder
build_flags = build_flags =
-DLV_CONF_PATH="../../../../src/conf/lv_conf.h" -DLV_CONF_PATH="../../../../src/conf/lv_conf.h"
-DUSER_SETUP_LOADED=1 extra_scripts =
-DILI9341_2_DRIVER=1 pre:extract_commit.py
-DTFT_WIDTH=240
-DTFT_HEIGHT=320 [env:esp32-2432S028R]
-DTFT_BL=21 board = esp32-2432S028R
-DTFT_BACKLIGHT_ON=HIGH lib_deps =
-DTFT_MISO=12 SPI
-DTFT_MOSI=13 https://github.com/suchmememanyskill/lvgl
-DTFT_SCLK=14 https://github.com/Bodmer/TFT_eSPI.git
-DTFT_CS=15 https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
-DTFT_DC=2 bblanchon/ArduinoJson@^7.0.0
-DTFT_RST=-1 plageoj/UrlEncode@^1.0.1
-DLOAD_GCLD=1 knolleary/PubSubClient@^2.8
-DLOAD_FONT2=1 WiFiClientSecure
-DLOAD_GFXFF=1
-DSMOOTH_FONT=1 [env:esp32-3248S035C]
-DSPI_FREQUENCY=55000000 board = esp32-3248S035C
-DSPI_READ_FREQUENCY=20000000 lib_deps =
-DSPI_TOUCH_FREQUENCY=2500000 SPI
https://github.com/suchmememanyskill/lvgl
https://github.com/Bodmer/TFT_eSPI.git
https://github.com/OperatorB/gt911-arduino-fixed-reset.git
bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
WiFiClientSecure
[env:esp32-3248S035C-V]
board = esp32-3248S035C-vertical
lib_deps =
SPI
https://github.com/suchmememanyskill/lvgl
https://github.com/Bodmer/TFT_eSPI.git
https://github.com/OperatorB/gt911-arduino-fixed-reset.git
bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
WiFiClientSecure
[env:esp32-2432S024C-SD]
board = esp32-2432S024C-smartdisplay
[env:esp32-2432S028Rv1-SD]
board = esp32-2432S028RV1-smartdisplay
[env:esp32-2432S028Rv2-SD]
board = esp32-2432S028RV2-smartdisplay
[env:esp32-2432S028Rv3-SD]
board = esp32-2432S028RV3-smartdisplay
[env:esp32-2432S032C-SD]
board = esp32-2432S032C-smartdisplay
[env:esp32-3248S035C-SD]
board = esp32-3248S035C-smartdisplay
[env:esp32-4827S043C-SD]
board = esp32-4827S043C-smartdisplay
[env:esp32-4827S043R-SD]
board = esp32-4827S043R-smartdisplay
[env:esp32-8048S043C-SD]
board = esp32-8048S043C-smartdisplay
[env:esp32-8048S043C-SD-alt]
board = esp32-8048S043C-smartdisplay-alt
[env:esp32-2432S022C-SD-V]
board = esp32-2432S022C-vertical
[env:esp32-CROWPANEL-28R]
board = esp32-CROWPANEL-28R
lib_deps =
SPI
https://github.com/suchmememanyskill/lvgl
https://github.com/Bodmer/TFT_eSPI.git
bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
WiFiClientSecure
[env:esp32-CROWPANEL-35C]
board = esp32-CROWPANEL-35C
lib_deps =
SPI
https://github.com/suchmememanyskill/lvgl
https://github.com/lovyan03/LovyanGFX@1.2.0
bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
WiFiClientSecure

View File

@@ -2,48 +2,204 @@
#include "global_config.h" #include "global_config.h"
#include "lvgl.h" #include "lvgl.h"
GLOBAL_CONFIG global_config = {0}; GlobalConfig global_config = {0};
TemporaryConfig temporary_config = {0};
COLOR_DEF color_defs[] = { ColorDefinition color_defs[] = {
{LV_PALETTE_BLUE, LV_PALETTE_RED}, {LV_PALETTE_BLUE, 0, LV_PALETTE_RED},
{LV_PALETTE_GREEN, LV_PALETTE_PURPLE}, {LV_PALETTE_GREEN, 0, LV_PALETTE_PURPLE},
{LV_PALETTE_GREY, LV_PALETTE_CYAN}, {LV_PALETTE_LIME, -2, LV_PALETTE_PURPLE},
{LV_PALETTE_YELLOW, LV_PALETTE_PINK}, {LV_PALETTE_GREY, 0, LV_PALETTE_CYAN},
{LV_PALETTE_ORANGE, LV_PALETTE_BLUE}, {LV_PALETTE_YELLOW, -2, LV_PALETTE_PINK},
{LV_PALETTE_RED, LV_PALETTE_GREEN}, {LV_PALETTE_ORANGE, -2, LV_PALETTE_BLUE},
{LV_PALETTE_PURPLE, LV_PALETTE_GREY}, {LV_PALETTE_RED, 0, LV_PALETTE_BLUE},
{LV_PALETTE_PURPLE, 0, LV_PALETTE_CYAN},
}; };
void WriteGlobalConfig() { void write_global_config()
{
Preferences preferences; Preferences preferences;
preferences.begin("global_config", false); preferences.begin("global_config", false);
preferences.putBytes("global_config", &global_config, sizeof(global_config)); preferences.putBytes("global_config", &global_config, sizeof(global_config));
preferences.end(); preferences.end();
} }
void VerifyVersion(){ void verify_version()
{
Preferences preferences; Preferences preferences;
if (!preferences.begin("global_config", false)) if (!preferences.begin("global_config", false))
return; return;
GLOBAL_CONFIG config = {0}; GlobalConfig 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();
} }
preferences.end(); preferences.end();
} }
void LoadGlobalConfig() { PrinterConfiguration* get_current_printer_config()
{
return &global_config.printer_config[global_config.printer_index];
}
int global_config_get_printer_config_count()
{
int count = 0;
for (int i = 0; i < PRINTER_CONFIG_COUNT; i++) {
if (global_config.printer_config[i].setup_complete)
count++;
}
return count;
}
int get_printer_config_free_index()
{
for (int i = 0; i < PRINTER_CONFIG_COUNT; i++) {
if (!global_config.printer_config[i].setup_complete)
return i;
}
return -1;
}
void global_config_add_new_printer()
{
int free_index = get_printer_config_free_index();
if (free_index <= -1)
{
LOG_LN("No available slot for new printer");
return;
}
PrinterConfiguration* old_config = &global_config.printer_config[global_config.printer_index];
PrinterConfiguration* new_config = &global_config.printer_config[free_index];
new_config->raw = old_config->raw;
new_config->setup_complete = false;
new_config->ip_configured = false;
new_config->auth_configured = false;
new_config->printer_type = PrinterType::PrinterTypeNone;
new_config->printer_name[0] = 0;
new_config->printer_host[0] = 0;
new_config->printer_auth[0] = 0;
new_config->klipper_port = 0;
new_config->color_scheme = old_config->color_scheme;
// TODO: Replace with memcpy
for (int i = 0; i < 3; i++){
new_config->hotend_presets[i] = old_config->hotend_presets[i];
new_config->bed_presets[i] = old_config->bed_presets[i];
}
for (int i = 0; i < 3; i++){
new_config->printer_move_x_steps[i] = old_config->printer_move_x_steps[i];
new_config->printer_move_y_steps[i] = old_config->printer_move_y_steps[i];
new_config->printer_move_z_steps[i] = old_config->printer_move_z_steps[i];
}
global_config_set_printer(free_index);
ESP.restart();
}
void global_config_set_printer(int idx)
{
if (idx < 0 || idx >= PRINTER_CONFIG_COUNT || global_config.printer_index == idx)
return;
global_config.printer_index = idx;
write_global_config();
}
void global_config_delete_printer(int idx)
{
if (global_config.printer_index == idx)
{
return;
}
PrinterConfiguration* config = &global_config.printer_config[idx];
config->setup_complete = false;
write_global_config();
ESP.restart();
}
void set_printer_config_index(int index)
{
if (index < 0 || index >= PRINTER_CONFIG_COUNT)
return;
PrinterConfiguration* old_config = &global_config.printer_config[global_config.printer_index];
PrinterConfiguration* new_config = &global_config.printer_config[index];
global_config.printer_index = index;
if (!new_config->ip_configured){
new_config->raw = old_config->raw;
new_config->ip_configured = false;
new_config->auth_configured = false;
new_config->printer_name[0] = 0;
new_config->printer_host[0] = 0;
new_config->printer_auth[0] = 0;
new_config->klipper_port = 0;
new_config->color_scheme = old_config->color_scheme;
// TODO: Replace with memcpy
for (int i = 0; i < 3; i++){
new_config->hotend_presets[i] = old_config->hotend_presets[i];
new_config->bed_presets[i] = old_config->bed_presets[i];
}
for (int i = 0; i < 3; i++){
new_config->printer_move_x_steps[i] = old_config->printer_move_x_steps[i];
new_config->printer_move_y_steps[i] = old_config->printer_move_y_steps[i];
new_config->printer_move_z_steps[i] = old_config->printer_move_z_steps[i];
}
write_global_config();
ESP.restart();
}
write_global_config();
}
void load_global_config()
{
global_config.version = CONFIG_VERSION; global_config.version = CONFIG_VERSION;
global_config.brightness = 255; global_config.brightness = 255;
global_config.screenTimeout = 5; global_config.screen_timeout = 5;
VerifyVersion(); global_config.printer_config[0].hotend_presets[0] = 0;
global_config.printer_config[0].hotend_presets[1] = 200;
global_config.printer_config[0].hotend_presets[2] = 240;
global_config.printer_config[0].bed_presets[0] = 0;
global_config.printer_config[0].bed_presets[1] = 60;
global_config.printer_config[0].bed_presets[2] = 70;
global_config.printer_config[0].printer_move_x_steps[0] = 10;
global_config.printer_config[0].printer_move_x_steps[1] = 100;
global_config.printer_config[0].printer_move_x_steps[2] = 1000;
global_config.printer_config[0].printer_move_y_steps[0] = 10;
global_config.printer_config[0].printer_move_y_steps[1] = 100;
global_config.printer_config[0].printer_move_y_steps[2] = 1000;
global_config.printer_config[0].printer_move_z_steps[0] = 1;
global_config.printer_config[0].printer_move_z_steps[1] = 10;
global_config.printer_config[0].printer_move_z_steps[2] = 100;
verify_version();
Preferences preferences; Preferences preferences;
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

@@ -3,48 +3,136 @@
#include "lvgl.h" #include "lvgl.h"
#define CONFIG_VERSION 2 #define CONFIG_VERSION 7
#define PRINTER_CONFIG_COUNT 6
#define DISPLAY_SECRETS 0
typedef struct _GLOBAL_CONFIG { enum {
unsigned char version; REMAINING_TIME_CALC_PERCENTAGE = 0,
REMAINING_TIME_CALC_INTERPOLATED = 1,
REMAINING_TIME_CALC_SLICER = 2,
};
enum {
SHOW_STATS_ON_PROGRESS_PANEL_NONE = 0,
SHOW_STATS_ON_PROGRESS_PANEL_LAYER = 1,
SHOW_STATS_ON_PROGRESS_PANEL_PARTIAL = 2,
SHOW_STATS_ON_PROGRESS_PANEL_ALL = 3,
};
enum PrinterType {
PrinterTypeNone = 0,
PrinterTypeKlipper = 1,
PrinterTypeKlipperSerial = 2,
PrinterTypeBambuLocal = 3,
PrinterTypeBambuCloud = 3,
PrinterTypeOctoprint = 4,
};
typedef struct {
union { union {
unsigned char raw; unsigned int raw;
struct { struct {
bool screenCalibrated : 1; // Internal
bool wifiConfigured : 1; bool setup_complete : 1;
bool ipConfigured : 1; bool ip_configured : 1;
bool lightMode : 1; bool auth_configured : 1;
bool invertColors : 1; bool custom_filament_move_macros : 1;
bool rotateScreen : 1; PrinterType printer_type : 3;
bool onDuringPrint : 1;
// External
bool light_mode : 1;
bool invert_colors : 1;
unsigned char remaining_time_calc_mode : 2;
unsigned char show_stats_on_progress_panel : 2;
}; };
}; };
float screenCalXOffset;
float screenCalXMult;
float screenCalYOffset;
float screenCalYMult;
char wifiSSID[32]; char printer_name[25];
char wifiPassword[64]; char printer_host[65];
char printer_auth[65];
char klipperHost[64]; unsigned int klipper_port;
unsigned short klipperPort;
unsigned char color_scheme; unsigned char color_scheme;
unsigned char brightness;
unsigned char screenTimeout; unsigned short hotend_presets[3];
} GLOBAL_CONFIG; unsigned short bed_presets[3];
unsigned short printer_move_x_steps[3];
unsigned short printer_move_y_steps[3];
unsigned short printer_move_z_steps[3];
} PrinterConfiguration;
typedef struct {
unsigned char version;
union {
unsigned int raw;
struct {
// Internal
bool screen_calibrated : 1;
bool wifi_configured : 1;
bool wifi_configuration_skipped : 1;
// External
bool rotate_screen : 1;
bool auto_ota_update : 1;
bool multi_printer_mode : 1;
bool on_during_print : 1;
bool display_mode : 1; // Driver specifc usage. Currently only used on ESP32-2432S028R to fix the screen on the usb-c model
bool disable_m117_messaging : 1;
bool sort_macros : 1;
bool show_estop : 1;
bool full_filenames : 1;
bool double_size_gcode_img : 1;
};
};
PrinterConfiguration printer_config[PRINTER_CONFIG_COUNT];
float screen_cal_x_offset;
float screen_cal_x_mult;
float screen_cal_y_offset;
float screen_cal_y_mult;
char wifi_SSID[33];
char wifi_password[65];
typedef struct _COLOR_DEF { unsigned char brightness;
unsigned char screen_timeout;
unsigned char printer_index;
} GlobalConfig;
// Volatile/temporary config that doesn't survive a reset
typedef struct {
bool debug : 1;
bool remote_echo : 1;
} TemporaryConfig;
typedef struct {
lv_palette_t primary_color; lv_palette_t primary_color;
short primary_color_light;
lv_palette_t secondary_color; lv_palette_t secondary_color;
} COLOR_DEF; } ColorDefinition;
extern GLOBAL_CONFIG global_config; extern GlobalConfig global_config;
extern COLOR_DEF color_defs[]; extern TemporaryConfig temporary_config;
extern ColorDefinition color_defs[];
void WriteGlobalConfig(); #define LOG(x) if(temporary_config.debug){ Serial.print(x);}
void VerifyVersion(); #define LOG_LN(x) if(temporary_config.debug){ Serial.println(x);}
void LoadGlobalConfig(); #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();
void global_config_add_new_printer();
void global_config_set_printer(int idx);
void global_config_delete_printer(int idx);
//PRINTER_CONFIG* get_current_printer_config();
int global_config_get_printer_config_count();
//void set_printer_config_index(int index);
//int get_printer_config_free_index();
#endif // !_GLOBAL_CONFIG_INIT #endif // !_GLOBAL_CONFIG_INIT

View File

@@ -49,7 +49,7 @@
#define LV_MEM_CUSTOM 0 #define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0 #if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/ #define LV_MEM_SIZE (40U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/ #define LV_MEM_ADR 0 /*0: unused*/
@@ -194,7 +194,7 @@
*-----------*/ *-----------*/
/*Enable the log module*/ /*Enable the log module*/
#define LV_USE_LOG 1 #define LV_USE_LOG 0
#if LV_USE_LOG #if LV_USE_LOG
/*How important log should be added: /*How important log should be added:
@@ -204,7 +204,7 @@
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user *LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/ *LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_USER #define LV_LOG_LEVEL LV_LOG_LEVEL_NONE
/*1: Print the log with 'printf'; /*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/ *0: User need to register a callback with `lv_log_register_print_cb()`*/
@@ -242,8 +242,12 @@
* Others * Others
*-----------*/ *-----------*/
#ifndef REPO_DEVELOPMENT
#define REPO_DEVELOPMENT 0
#endif
/*1: Show CPU usage and FPS count*/ /*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 0 #define LV_USE_PERF_MONITOR REPO_DEVELOPMENT
#if LV_USE_PERF_MONITOR #if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif #endif
@@ -327,12 +331,12 @@
*https://fonts.google.com/specimen/Montserrat*/ *https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0 #define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 1 #define LV_FONT_MONTSERRAT_10 1
#define LV_FONT_MONTSERRAT_12 0 #define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1 #define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 0 #define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 0 #define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0 #define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0 #define LV_FONT_MONTSERRAT_22 1
#define LV_FONT_MONTSERRAT_24 0 #define LV_FONT_MONTSERRAT_24 0
#define LV_FONT_MONTSERRAT_26 0 #define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 1 #define LV_FONT_MONTSERRAT_28 1
@@ -363,7 +367,7 @@
#define LV_FONT_CUSTOM_DECLARE #define LV_FONT_CUSTOM_DECLARE
/*Always set a default font*/ /*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_montserrat_14 #define LV_FONT_DEFAULT &CYD_SCREEN_FONT
/*Enable handling large font and/or fonts with a lot of characters. /*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp. *The limit depends on the font size, font face and bpp.
@@ -497,7 +501,7 @@
#define LV_USE_MSG 1 #define LV_USE_MSG 1
#define LV_USE_CHART 0 #define LV_USE_CHART 1
#define LV_USE_COLORWHEEL 0 #define LV_USE_COLORWHEEL 0
@@ -603,7 +607,7 @@
#endif #endif
/*PNG decoder library*/ /*PNG decoder library*/
#define LV_USE_PNG 0 #define LV_USE_PNG 1
/*BMP decoder library*/ /*BMP decoder library*/
#define LV_USE_BMP 0 #define LV_USE_BMP 0
@@ -674,16 +678,16 @@
====================*/ ====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ /*Show some widget. It might be required to increase `LV_MEM_SIZE` */
#define LV_USE_DEMO_WIDGETS 1 #define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS #if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 1 #define LV_DEMO_WIDGETS_SLIDESHOW 1
#endif #endif
/*Demonstrate the usage of encoder and keyboard*/ /*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 1 #define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/ /*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 1 #define LV_USE_DEMO_BENCHMARK 0
/*Stress test for LVGL*/ /*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0 #define LV_USE_DEMO_STRESS 0

View File

@@ -0,0 +1,371 @@
#include "bambu_printer_integration.hpp"
#include <PubSubClient.h>
WiFiClientSecure wifi_client;
PubSubClient client(wifi_client);
BambuPrinter* current_printer = NULL;
const char* COMMAND_FETCH_ALL = "{\"pushing\":{\"sequence_id\":\"0\",\"command\":\"pushall\",\"version\":1,\"push_target\":1}}";
const char* COMMAND_LIGHTCTL = "{\"system\":{\"sequence_id\":\"0\",\"command\":\"ledctrl\",\"led_node\":\"%s\",\"led_mode\":\"%s\"}}";
const char* COMMAND_SEND_GCODE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"gcode_line\",\"param\":\"%s\"}}";
const char* COMMAND_PRINT_STOP = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"stop\",\"param\":\"\"}}";
const char* COMMAND_PRINT_PAUSE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"pause\",\"param\":\"\"}}";
const char* COMMAND_PRINT_RESUME = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"resume\",\"param\":\"\"}}";
const char* COMMAND_FILAMENT_UNLOAD = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"unload_filament\"}}";
const char* COMMAND_FILAMENT_LOAD_EXTERNAL = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_change_filament\",\"target\":254,\"curr_temp\":215,\"tar_temp\":250}}";
const char* COMMAND_AMS_CONTOL_DONE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_control\",\"param\":\"done\"}}";
const char* COMMAND_AMS_CONTOL_RETRY = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_control\",\"param\":\"resume\"}}";
static void callback(char* topic, byte* payload, unsigned int length)
{
if (current_printer != NULL)
{
current_printer->receive_data(payload, length);
}
}
void BambuPrinter::receive_data(unsigned char* data, unsigned int length)
{
data[length] = 0;
JsonDocument doc;
deserializeJson(doc, data);
parse_state(doc);
}
bool BambuPrinter::publish_mqtt_command(const char* command)
{
if (!client.connected())
{
return false;
}
char auth[48] = {0};
sprintf(auth, "device/%s/request", printer_config->printer_auth);
LOG_F(("Publishing MQTT Command: %s\n", command));
return client.publish(auth, command);
}
bool BambuPrinter::move_printer(const char* axis, float amount, bool relative)
{
if (!printer_data.homed_axis || printer_data.state == PrinterStatePrinting)
return false;
char gcode[64];
const char* extra = (amount > 0) ? "+" : "";
const char* start = "";
const char* end = "";
if (relative) {
start = "G91\n";
}
else {
start = "G90\n";
}
sprintf(gcode, "%sG1 %s%s%.3f F6000", start, axis, extra, amount);
send_gcode(gcode);
return true;
}
bool BambuPrinter::execute_feature(PrinterFeatures feature)
{
switch (feature)
{
case PrinterFeatureHome:
return send_gcode("G28");
case PrinterFeatureDisableSteppers:
return send_gcode("M18 X Y Z");
case PrinterFeaturePause:
return publish_mqtt_command(COMMAND_PRINT_PAUSE);
case PrinterFeatureResume:
return publish_mqtt_command(COMMAND_PRINT_RESUME);
case PrinterFeatureStop:
return publish_mqtt_command(COMMAND_PRINT_STOP);
case PrinterFeatureExtrude:
if (printer_data.state == PrinterStatePrinting)
{
return false;
}
return send_gcode("M83\nG1 E25 F300");
case PrinterFeatureRetract:
if (printer_data.state == PrinterStatePrinting)
{
return false;
}
return send_gcode("M83\nG1 E-25 F300");
case PrinterFeatureCooldown:
return send_gcode("M104 S0\nM140 S0");
case PrinterFeatureContinueError:
return publish_mqtt_command(COMMAND_AMS_CONTOL_DONE);
case PrinterFeatureRetryError:
return publish_mqtt_command(COMMAND_AMS_CONTOL_RETRY);
case PrinterFeatureIgnoreError:
ignore_error = last_error;
publish_mqtt_command(COMMAND_FETCH_ALL);
return true;
default:
LOG_F(("Unsupported printer feature %d", feature));
}
return false;
}
bool BambuPrinter::connect()
{
wifi_client.setInsecure();
wifi_client.setTimeout(3);
client.setBufferSize(4096);
client.setServer(printer_config->printer_host, 8883);
current_printer = this;
client.setCallback(NULL);
char buff[10] = {0};
sprintf(buff, "%d", printer_config->klipper_port);
if (!client.connect("id", "bblp", buff))
{
LOG_LN("Bambu: Wrong IP or LAN code.");
return false;
}
char auth[48] = {0};
sprintf(auth, "device/%s/report", printer_config->printer_auth);
if (!client.subscribe(auth))
{
LOG_LN("Bambu: Wrong serial number.");
return false;
}
delay(100);
client.loop();
if (!client.connected())
{
LOG_LN("Bambu: Connection lost. Likely wrong serial number.");
return false;
}
client.setCallback(callback);
printer_data.state = PrinterState::PrinterStateIdle;
return publish_mqtt_command(COMMAND_FETCH_ALL);
}
void BambuPrinter::disconnect()
{
current_printer = NULL;
printer_data.state = PrinterState::PrinterStateOffline;
client.disconnect();
client.setCallback(NULL);
client.setBufferSize(16);
}
bool BambuPrinter::fetch()
{
if (!client.connected())
{
LOG_LN("Failed to fetch printer data: Not connected");
return false;
}
if (!client.loop())
{
LOG_LN("Failed to fetch printer data: Fetching data failed");
return false;
}
return true;
}
PrinterDataMinimal BambuPrinter::fetch_min()
{
PrinterDataMinimal min = {};
min.success = true;
min.state = PrinterState::PrinterStateIdle;
min.print_progress = 0;
min.power_devices = 0;
return min;
}
const char * MACRO_UNLOAD = "Unload filament";
const char * MACRO_LOAD = "Load filament (External)";
Macros BambuPrinter::get_macros()
{
Macros macros = {0};
macros.success = true;
macros.count = get_macros_count();
macros.macros = (char **)malloc(sizeof(char *) * macros.count);
macros.macros[0] = (char *)malloc(25);
strcpy(macros.macros[0], MACRO_LOAD);
macros.macros[1] = (char *)malloc(16);
strcpy(macros.macros[1], MACRO_UNLOAD);
return macros;
}
int BambuPrinter::get_macros_count()
{
return 2;
}
bool BambuPrinter::execute_macro(const char* macro)
{
if (strcmp(macro, MACRO_LOAD) == 0)
{
return publish_mqtt_command(COMMAND_FILAMENT_LOAD_EXTERNAL);
}
else if (strcmp(macro, MACRO_UNLOAD) == 0)
{
return publish_mqtt_command(COMMAND_FILAMENT_UNLOAD);
}
return false;
}
const char* WORK_LIGHT = "Work Light";
const char* CHAMBER_LIGHT = "Chamber Light";
PowerDevices BambuPrinter::get_power_devices()
{
PowerDevices power_devices = {0};
power_devices.success = true;
int count = get_power_devices_count();
if (count == 0)
{
return power_devices;
}
power_devices.power_devices = (char **)malloc(sizeof(char *) * count);
power_devices.power_states = (bool *)malloc(sizeof(bool) * count);
if (work_light_available)
{
power_devices.power_devices[power_devices.count] = (char *)malloc(10 + 1);
strcpy(power_devices.power_devices[power_devices.count], WORK_LIGHT);
power_devices.power_states[power_devices.count] = work_light_on;
power_devices.count++;
}
if (chamber_light_available)
{
power_devices.power_devices[power_devices.count] = (char *)malloc(13 + 1);
strcpy(power_devices.power_devices[power_devices.count], CHAMBER_LIGHT);
power_devices.power_states[power_devices.count] = chamber_light_on;
power_devices.count++;
}
return power_devices;
}
int BambuPrinter::get_power_devices_count()
{
return (work_light_available ? 1 : 0) + (chamber_light_available ? 1 : 0);
}
bool BambuPrinter::set_power_device_state(const char* device_name, bool state)
{
char buff[128] = {0};
const char* device;
if (strcmp(device_name, WORK_LIGHT) == 0)
{
device = "work_light";
}
else if (strcmp(device_name, CHAMBER_LIGHT) == 0)
{
device = "chamber_light";
}
else
{
return false;
}
sprintf(buff, COMMAND_LIGHTCTL, device, state ? "on" : "off");
return publish_mqtt_command(buff);
}
Files BambuPrinter::get_files()
{
PrinterState state = printer_data.state;
disconnect();
Files files = parse_files(wifi_client, 20);
connect();
printer_data.state = state;
return files;
}
Thumbnail BambuPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
{
Thumbnail thumbnail = {0};
return thumbnail;
}
bool BambuPrinter::set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature)
{
char gcode[64] = {0};
switch (device)
{
case PrinterTemperatureDeviceBed:
sprintf(gcode, "M140 S%d", temperature);
break;
case PrinterTemperatureDeviceNozzle1:
sprintf(gcode, "M104 S%d", temperature);
break;
default:
LOG_F(("Unknown temperature device %d was requested to heat to %.2f", device, temperature));
return false;
}
return send_gcode(gcode);
}
bool BambuPrinter::send_gcode(const char* gcode, bool wait)
{
char* buff = (char *)malloc(strlen(gcode) + 70);
sprintf(buff, COMMAND_SEND_GCODE, gcode);
return publish_mqtt_command(buff);
}
BambuConnectionStatus connection_test_bambu(PrinterConfiguration* config)
{
WiFiClientSecure connection_test_wifi_client;
PubSubClient connection_test_client(connection_test_wifi_client);
connection_test_wifi_client.setInsecure();
connection_test_client.setServer(config->printer_host, 8883);
char buff[10] = {0};
sprintf(buff, "%d", config->klipper_port);
if (!connection_test_client.connect("id", "bblp", buff))
{
LOG_LN("Bambu: Wrong IP or LAN code.");
return BambuConnectionStatus::BambuConnectFail;
}
char auth[48] = {0};
sprintf(auth, "device/%s/report", config->printer_auth);
if (!connection_test_client.subscribe(auth))
{
LOG_LN("Bambu: Wrong serial number.");
return BambuConnectionStatus::BambuConnectSNFail;
}
delay(100);
connection_test_client.loop();
if (!connection_test_client.connected())
{
LOG_LN("Bambu: Connection lost. Likely wrong serial number.");
return BambuConnectionStatus::BambuConnectSNFail;
}
connection_test_client.disconnect();
LOG_LN("Bambu: Connection test successful!");
return BambuConnectionStatus::BambuConnectOk;
}

View File

@@ -0,0 +1,97 @@
#pragma once
#include "../printer_integration.hpp"
#include <ArduinoJson.h>
#include <WifiClientSecure.h>
enum BambuSpeedProfile
{
BambuSpeedProfileSilent = 1,
BambuSpeedProfileNormal = 2,
BambuSpeedProfileSport = 3,
BambuSpeedProfileLudicrous = 4,
};
class BambuPrinter : public BasePrinter
{
private:
unsigned int last_error = 0;
unsigned int ignore_error = 0;
unsigned long print_start;
protected:
void parse_state(JsonDocument& in);
void init_ui_panels();
Files parse_files(WiFiClientSecure& client, int max_files);
public:
float aux_fan_speed;
float chamber_fan_speed;
BambuSpeedProfile speed_profile = BambuSpeedProfileNormal;
union {
struct {
bool chamber_light_available : 1;
bool chamber_light_on : 1;
bool work_light_available : 1;
bool work_light_on : 1;
bool has_ams : 1;
};
unsigned char bambu_misc;
};
BambuPrinter(int index) : BasePrinter(index)
{
supported_features = PrinterFeatureHome
| PrinterFeatureDisableSteppers
| PrinterFeaturePause
| PrinterFeatureResume
| PrinterFeatureStop
| PrinterFeatureEmergencyStop
| PrinterFeatureCooldown
| PrinterFeatureContinueError
| PrinterFeatureExtrude
| PrinterFeatureRetract
| PrinterFeatureIgnoreError
| PrinterFeatureRetryError;
supported_temperature_devices = PrinterTemperatureDeviceBed
| PrinterTemperatureDeviceNozzle1;
popup_message_timeout_s = -1;
bambu_misc = 0;
printer_data.error_screen_features = PrinterFeatureRetryError | PrinterFeatureIgnoreError | PrinterFeatureContinueError;
print_start = millis();
init_ui_panels();
no_confirm_print_file = true;
}
bool move_printer(const char* axis, float amount, bool relative);
bool execute_feature(PrinterFeatures feature);
bool connect();
bool fetch();
PrinterDataMinimal fetch_min();
void disconnect();
Macros get_macros();
int get_macros_count();
bool execute_macro(const char* macro);
PowerDevices get_power_devices();
int get_power_devices_count();
bool set_power_device_state(const char* device_name, bool state);
Files get_files();
bool start_file(const char* filename);
Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);
bool send_gcode(const char* gcode, bool wait = true);
void receive_data(unsigned char* data, unsigned int length);
bool publish_mqtt_command(const char* command);
};
enum BambuConnectionStatus {
BambuConnectFail = 0,
BambuConnectOk = 1,
BambuConnectSNFail = 2,
};
BambuConnectionStatus connection_test_bambu(PrinterConfiguration* config);

View File

@@ -0,0 +1,257 @@
#include "bambu_printer_integration.hpp"
#include "lvgl.h"
#include "../../ui/ui_utils.h"
#include <stdio.h>
#include "../common/constants.h"
const char* speed_profiles[] = { "Silent (50%)", "Normal (100%)", "Sport (124%)", "Ludicrous (166%)" };
const BambuSpeedProfile speed_profile_values[] = { BambuSpeedProfileSilent, BambuSpeedProfileNormal, BambuSpeedProfileSport, BambuSpeedProfileLudicrous };
const char* COMMAND_SET_PRINT_SPEED = "{\"print\":{\"command\":\"print_speed\",\"param\":\"%d\"}}";
const char* COMMAND_START_PRINT_3MF = "{\"print\":{\"command\":\"project_file\",\"param\":\"Metadata/plate_1.gcode\",\"project_id\":\"0\",\"profile_id\":\"0\",\"task_id\":\"0\",\"subtask_id\":\"0\",\"subtask_name\":\"CYD-Klipper Print Job\",\"url\":\"file:///sdcard/%s\",\"timelapse\":%s,\"bed_type\":\"auto\",\"bed_leveling\":%s,\"flow_cali\":%s,\"vibration_cali\":%s,\"layer_inspect\":%s,\"ams_mapping\":[],\"use_ams\":%s}}";
enum FanIndex
{
PartFan = 1,
AuxFan = 2,
ChamberFan = 3,
};
static void set_fan_speed_text(lv_event_t* e, FanIndex index)
{
lv_obj_t * label = lv_event_get_target(e);
char data[16];
float fan_speed = 0;
const char* fan_type = "";
switch (index)
{
case PartFan:
fan_speed = get_current_printer_data()->fan_speed;
fan_type = "P.Fan";
break;
case AuxFan:
fan_speed = ((BambuPrinter*)get_current_printer)->aux_fan_speed;
fan_type = "A.Fan";
break;
case ChamberFan:
fan_speed = ((BambuPrinter*)get_current_printer)->chamber_fan_speed;
fan_type = "C.Fan";
break;
}
sprintf(data, "%s: %.0f%%", fan_type, get_current_printer_data()->fan_speed * 100);
lv_label_set_text(label, data);
}
static void set_fan_speed(lv_event_t* e, FanIndex index)
{
int speed = (int)lv_event_get_user_data(e);
int actual_speed = fan_percent_to_byte(speed);
BambuPrinter* printer = (BambuPrinter*)get_current_printer(); // TODO: pass by ref
char buff[20];
sprintf(buff, "M106 P%d S%d", index, actual_speed);
printer->send_gcode(buff);
}
static void set_part_fan_speed_text(lv_event_t * e)
{
set_fan_speed_text(e, FanIndex::PartFan);
}
static void set_part_fan_speed(lv_event_t * e)
{
set_fan_speed(e, FanIndex::PartFan);
}
FAN_SPEED_COLUMN(set_part_fan_speed, part_fan_speed_columns)
static void set_aux_fan_speed_text(lv_event_t * e)
{
set_fan_speed_text(e, FanIndex::AuxFan);
}
static void set_aux_fan_speed(lv_event_t * e)
{
set_fan_speed(e, FanIndex::AuxFan);
}
FAN_SPEED_COLUMN(set_aux_fan_speed, aux_fan_speed_columns)
static void set_chamber_fan_speed_text(lv_event_t * e)
{
set_fan_speed_text(e, FanIndex::ChamberFan);
}
static void set_chamber_fan_speed(lv_event_t * e)
{
set_fan_speed(e, FanIndex::ChamberFan);
}
FAN_SPEED_COLUMN(set_chamber_fan_speed, chamber_fan_speed_columns)
// TODO: move to common
static void set_speed_mult_text(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char buff[16];
sprintf(buff, "Speed: %.0f%%", get_current_printer_data()->speed_mult * 100);
lv_label_set_text(label, buff);
}
static void set_speed_mult(lv_event_t * e)
{
BambuSpeedProfile speed = (BambuSpeedProfile)((int)lv_event_get_user_data(e));
BambuPrinter* printer = (BambuPrinter*)get_current_printer(); // TODO: pass by ref
char buff[128];
sprintf(buff, COMMAND_SET_PRINT_SPEED, speed);
printer->publish_mqtt_command(buff);
}
lv_button_column_t speed_profile_columns[] = {
{ set_speed_mult, speed_profiles, (const void**)speed_profile_values, 4},
};
static void open_part_fan_speed_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_part_fan_speed_text, part_fan_speed_columns, 3);
}
static void open_aux_fan_speed_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_aux_fan_speed_text, aux_fan_speed_columns, 3);
}
static void open_chamber_fan_speed_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_chamber_fan_speed_text, chamber_fan_speed_columns, 3);
}
static void open_speed_mult_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_speed_mult_text, speed_profile_columns, 1);
}
static PrinterUiPanel bambu_ui_panels[4] {
{ .set_label = (void*)set_speed_mult_text, .open_panel = (void*)open_speed_mult_panel },
{ .set_label = (void*)set_part_fan_speed_text, .open_panel = (void*)open_part_fan_speed_panel },
{ .set_label = (void*)set_chamber_fan_speed_text, .open_panel = (void*)open_chamber_fan_speed_panel },
{ .set_label = (void*)set_aux_fan_speed_text, .open_panel = (void*)open_aux_fan_speed_panel },
};
void BambuPrinter::init_ui_panels()
{
custom_menus_count = 4;
custom_menus = bambu_ui_panels;
}
struct
{
const char* bambu_current_file;
union
{
struct
{
bool bambu_option_use_ams : 1;
bool bambu_option_timelapse : 1;
bool bambu_option_bed_leveling : 1;
bool bambu_option_flow_calibration : 1;
bool bambu_option_vibration_compensation : 1;
bool bambu_option_layer_inspect : 1;
};
unsigned char bambu_options_raw;
};
} __internal_bambu_file_state = {};
#define SET_BOOL_STATE(bool_name, func_name) static void func_name (lv_event_t * e) { auto state = lv_obj_get_state(lv_event_get_target(e)); bool_name = (state & LV_STATE_CHECKED == LV_STATE_CHECKED); }
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_use_ams, set_bambu_option_use_ams)
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_timelapse, set_bambu_option_timelapse)
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_bed_leveling, set_bambu_option_bed_leveling)
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_flow_calibration, set_bambu_option_flow_calibration)
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_vibration_compensation, set_bambu_option_vibration_compensation)
SET_BOOL_STATE(__internal_bambu_file_state.bambu_option_layer_inspect, set_bambu_option_layer_inspect)
#define BOOLEAN_TO_STRING(b) b ? "true" : "false"
static void print_file_start(lv_event_t * e)
{
BambuPrinter* printer = (BambuPrinter*)get_current_printer();
char buff[713];
if (snprintf(buff, 713, COMMAND_START_PRINT_3MF,
__internal_bambu_file_state.bambu_current_file,
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_timelapse),
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_bed_leveling),
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_flow_calibration),
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_vibration_compensation),
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_layer_inspect),
BOOLEAN_TO_STRING(__internal_bambu_file_state.bambu_option_use_ams)) >= 712)
{
LOG_LN("Failed to prepare message to start print");
return;
}
printer->publish_mqtt_command(buff);
}
bool BambuPrinter::start_file(const char* filename){
if (get_current_printer_data()->state != PrinterState::PrinterStateIdle)
{
return false;
}
__internal_bambu_file_state.bambu_current_file = filename;
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX * 2, 0);
lv_layout_flex_column(panel);
lv_obj_set_size(panel, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * label_print_file = lv_label_create(panel);
lv_obj_set_width(label_print_file, LV_PCT(100));
lv_label_set_long_mode(label_print_file, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text_fmt(label_print_file, "Settings for %s", __internal_bambu_file_state.bambu_current_file);
__internal_bambu_file_state.bambu_option_use_ams = ((BambuPrinter*)get_current_printer())->has_ams;
__internal_bambu_file_state.bambu_option_timelapse = false;
__internal_bambu_file_state.bambu_option_bed_leveling = true;
__internal_bambu_file_state.bambu_option_flow_calibration = true;
__internal_bambu_file_state.bambu_option_vibration_compensation = true;
__internal_bambu_file_state.bambu_option_layer_inspect = true;
if (__internal_bambu_file_state.bambu_option_use_ams)
{
lv_create_custom_menu_switch("Use AMS", panel, set_bambu_option_use_ams, __internal_bambu_file_state.bambu_option_use_ams);
}
lv_create_custom_menu_switch("Timelapse", panel, set_bambu_option_timelapse, __internal_bambu_file_state.bambu_option_timelapse);
lv_create_custom_menu_switch("Bed Leveling", panel, set_bambu_option_bed_leveling, __internal_bambu_file_state.bambu_option_bed_leveling);
lv_create_custom_menu_switch("Flow Calibration", panel, set_bambu_option_flow_calibration, __internal_bambu_file_state.bambu_option_flow_calibration);
lv_create_custom_menu_switch("Vibration Compensation", panel, set_bambu_option_vibration_compensation, __internal_bambu_file_state.bambu_option_vibration_compensation);
lv_create_custom_menu_switch("Inspect First Layer", panel, set_bambu_option_layer_inspect, __internal_bambu_file_state.bambu_option_layer_inspect);
lv_obj_t* buttons_panel = lv_create_empty_panel(panel);
lv_layout_flex_row(buttons_panel);
lv_obj_set_size(buttons_panel, LV_PCT(100), CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t* btn = lv_btn_create(buttons_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, panel);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE " Cancel");
lv_obj_center(label);
btn = lv_btn_create(buttons_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, print_file_start, LV_EVENT_CLICKED, panel);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_OK " Print");
lv_obj_center(label);
return true;
}

View File

@@ -0,0 +1,398 @@
#include "bambu_printer_integration.hpp"
#include <HTTPClient.h>
#include <list>
#define BIT_X_AXIS_HOMED BIT(0)
#define BIT_Y_AXIS_HOMED BIT(1)
#define BIT_Z_AXIS_HOMED BIT(2)
float convert_fan_speed(const char* in)
{
if (in == NULL || strlen(in) <= 0)
{
return 0;
}
int part_value = atoi(in);
float percentage = (part_value / 15.0f) * 100;
return round(percentage / 10) / 10;
}
void BambuPrinter::parse_state(JsonDocument& in)
{
if (!in.containsKey("print"))
{
return;
}
JsonObject print = in["print"];
if (print.containsKey("print_error"))
{
unsigned int error = print["print_error"].as<unsigned int>();
if (error != last_error)
{
last_error = error;
if (error > 0)
{
HTTPClient client;
client.setTimeout(1000);
client.setConnectTimeout(1000);
LOG_F(("Free heap: %d bytes\n", esp_get_free_heap_size()))
char buff[10] = {0};
sprintf(buff, "%X_%X", error >> 16, error & 0xFFFF);
int http_status_code = 0;
try
{
client.begin("http://bambu.suchmeme.nl/" + String(buff));
LOG_F(("Sending request to http://bambu.suchmeme.nl/%s", buff));
http_status_code = client.GET();
LOG_F(("Response: %d", http_status_code));
}
catch (...)
{
LOG_LN("Error downloading error code page");
}
if (http_status_code == 200)
{
printer_data.state_message = (char *)malloc(client.getSize() + 20);
sprintf(printer_data.state_message, "%s: %s", buff, client.getString().c_str());
}
else
{
printer_data.state_message = (char *)malloc(20);
sprintf(printer_data.state_message, "Error: %s", buff);
}
}
}
}
if (print.containsKey("nozzle_temper"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] = print["nozzle_temper"];
printer_data.can_extrude = printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] >= MIN_EXTRUDER_EXTRUDE_TEMP;
}
if (print.containsKey("nozzle_target_temper"))
{
printer_data.target_temperatures[PrinterTemperatureDeviceIndexNozzle1] = print["nozzle_target_temper"];
}
if (print.containsKey("bed_temper"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexBed] = print["bed_temper"];
}
if (print.containsKey("bed_target_temper"))
{
printer_data.target_temperatures[PrinterTemperatureDeviceIndexBed] = print["bed_target_temper"];
}
if (print.containsKey("spd_lvl"))
{
int speed_profile_int = print["spd_lvl"];
speed_profile = (BambuSpeedProfile)speed_profile_int;
switch (speed_profile)
{
case BambuSpeedProfileSilent:
printer_data.speed_mult = 0.5f;
break;
case BambuSpeedProfileNormal:
printer_data.speed_mult = 1.0f;
break;
case BambuSpeedProfileSport:
printer_data.speed_mult = 1.24f;
break;
case BambuSpeedProfileLudicrous:
printer_data.speed_mult = 1.66f;
break;
}
}
if (print.containsKey("home_flag"))
{
unsigned int home_flag = print["home_flag"].as<unsigned int>();
printer_data.homed_axis = (home_flag & (BIT_X_AXIS_HOMED | BIT_Y_AXIS_HOMED | BIT_Z_AXIS_HOMED)) == (BIT_X_AXIS_HOMED | BIT_Y_AXIS_HOMED | BIT_Z_AXIS_HOMED);
}
if (last_error > 0 && last_error != ignore_error)
{
printer_data.state = PrinterState::PrinterStateError;
}
else if (print.containsKey("gcode_state"))
{
const char* state = print["gcode_state"];
if (strcasecmp(state, "pause") == 0)
{
printer_data.state = PrinterState::PrinterStatePaused;
}
else if (strcasecmp(state, "running") == 0 || strcasecmp(state, "prepare") == 0)
{
printer_data.state = PrinterState::PrinterStatePrinting;
}
else
{
printer_data.state = PrinterState::PrinterStateIdle;
}
}
if (print.containsKey("mc_remaining_time"))
{
printer_data.remaining_time_s = print["mc_remaining_time"];
printer_data.remaining_time_s *= 60;
if (printer_data.remaining_time_s > 300)
{
print_start = millis() - (printer_data.remaining_time_s / (1 - printer_data.print_progress) * printer_data.print_progress * 1000);
}
}
if (print.containsKey("mc_percent"))
{
printer_data.print_progress = print["mc_percent"];
printer_data.print_progress /= 100;
}
if (printer_data.state == PrinterState::PrinterStatePrinting)
{
printer_data.elapsed_time_s = (millis() - print_start) / 1000;
}
if (print.containsKey("layer_num"))
{
printer_data.current_layer = print["layer_num"];
}
if (print.containsKey("total_layer_num"))
{
printer_data.total_layers = print["total_layer_num"];
}
if (print.containsKey("lights_report"))
{
for (JsonObject lights : print["lights_report"].as<JsonArray>())
{
if (lights.containsKey("node") && lights.containsKey("mode"))
{
bool mode = !(lights["mode"] == "off");
const char* node = lights["node"];
if (node == NULL)
{
continue;
}
if (strcmp(node, "chamber_light") == 0)
{
chamber_light_available = true;
chamber_light_on = mode;
}
else if (strcmp(node, "work_light") == 0)
{
work_light_available = true;
work_light_on = mode;
}
}
}
}
if (print.containsKey("gcode_file"))
{
const char* filename = print["gcode_file"];
if (filename != NULL && (printer_data.print_filename == NULL || strcmp(printer_data.print_filename, filename)))
{
printer_data.print_filename = (char *)malloc(strlen(filename) + 1);
strcpy(printer_data.print_filename, filename);
}
}
if (print.containsKey("cooling_fan_speed"))
{
printer_data.fan_speed = convert_fan_speed(print["cooling_fan_speed"]);
}
if (print.containsKey("big_fan1_speed"))
{
aux_fan_speed = convert_fan_speed(print["big_fan1_speed"]);
}
if (print.containsKey("big_fan2_speed"))
{
chamber_fan_speed = convert_fan_speed(print["big_fan2_speed"]);
}
if (print.containsKey("ams_exist_bits"))
{
const char* ams_exists = print["ams_exist_bits"];
if (!strcmp(ams_exists, "0"))
{
has_ams = true;
}
}
printer_data.extrude_mult = 1;
}
// Derived from https://github.com/ldab/ESP32_FTPClient/blob/master/src/ESP32_FTPClient.cpp
bool wifi_client_response_pass(WiFiClientSecure& client)
{
unsigned long _m = millis();
bool first_char = true;
while (!client.available() && millis() < _m + 500) delay(1);
if(!client.available())
{
LOG_LN("FTPS: No response from server");
return false;
}
LOG_LN("[FTPS response]");
bool response = true;
while (client.available())
{
char byte = client.read();
if (first_char && (byte == '4' || byte == '5'))
{
LOG_LN("FTPS: Server returned an error");
response = false;
}
first_char = false;
LOG_F(("%c", byte));
}
return response;
}
bool wifi_client_response_parse(WiFiClientSecure& client, std::list<char*> &files, int max_files)
{
unsigned long _m = millis();
while (!client.available() && millis() < _m + 500) delay(1);
if(!client.available())
{
LOG_LN("FTPS: No response from server");
return false;
}
LOG_LN("[FTPS response]");
char buff[128] = {0};
int index = 0;
while (client.available()) {
int byte = client.read();
LOG_F(("%c", byte));
buff[index] = byte;
if (byte == '\n' || byte == '\r' || byte <= 0)
{
buff[index] = 0;
if (index > 10)
{
char* file = (char*)malloc(index + 1);
if (file != NULL)
{
strcpy(file, buff);
files.push_front(file);
if (files.size() > max_files)
{
auto last_entry = files.back();
if (last_entry != NULL)
free(last_entry);
files.pop_back();
}
}
else
{
LOG_LN("Failed to allocate memory");
}
}
index = 0;
}
else
{
index++;
}
}
return true;
}
bool send_command_without_response(WiFiClientSecure& client, const char* command)
{
client.println(command);
LOG_F(("[FTPS Command] %s\n", command));
return wifi_client_response_pass(client);
}
// TODO: This isn't a 'pure' parser implementation. Remove network calls, only do parsing
Files BambuPrinter::parse_files(WiFiClientSecure& wifi_client, int max_files)
{
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
unsigned long timer_request = millis();
Files result = {0};
if (!wifi_client.connect(printer_config->printer_host, 990))
{
LOG_LN("Failed to fetch files: connection failed");
}
wifi_client_response_pass(wifi_client);
char auth_code_buff[16] = {0};
sprintf(auth_code_buff, "PASS %d", printer_config->klipper_port);
send_command_without_response(wifi_client, "USER bblp");
wifi_client_response_pass(wifi_client);
send_command_without_response(wifi_client, auth_code_buff);
send_command_without_response(wifi_client, "PASV");
send_command_without_response(wifi_client, "NLST");
wifi_client.stop();
if (wifi_client.connect(printer_config->printer_host, 2024))
{
unsigned long timer_parse = millis();
std::list<char*> files;
wifi_client_response_parse(wifi_client, files, max_files);
result.available_files = (char**)malloc(sizeof(char*) * files.size());
if (result.available_files == NULL)
{
LOG_LN("Failed to allocate memory");
for (auto file : files){
free(file);
}
return result;
}
for (auto file : files){
result.available_files[result.count++] = file;
}
result.success = true;
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))
}
else
{
LOG_LN("Failed to fetch files: data connection failed");
}
wifi_client.stop();
return result;
}

View File

@@ -0,0 +1,15 @@
#include "constants.h"
const char* fan_speeds_col_1[] = { "On\n100%", "Off\n0%" };
const int fan_speeds_col_1_values[] = { 100, 0 };
const char* fan_speeds_col_2[] = { "10%", "20%", "30%", "40%", "50%"};
const int fan_speeds_col_2_values[] = { 10, 20, 30, 40, 50 };
const char* fan_speeds_col_3[] = { "60%", "70%", "80%", "90%"};
const int fan_speeds_col_3_values[] = { 60, 70, 80, 90 };
unsigned char fan_percent_to_byte(int percent)
{
return percent * 255 / 100;
}

View File

@@ -0,0 +1,14 @@
#pragma once
extern const char* fan_speeds_col_1[];
extern const int fan_speeds_col_1_values[];
extern const char* fan_speeds_col_2[];
extern const int fan_speeds_col_2_values[];
extern const char* fan_speeds_col_3[];
extern const int fan_speeds_col_3_values[];
unsigned char fan_percent_to_byte(int percent);
#define FAN_SPEED_COLUMN(set_fan_speed, column_name) lv_button_column_t column_name[] = {{ set_fan_speed, fan_speeds_col_2, (const void**)fan_speeds_col_2_values, 5},{ set_fan_speed, fan_speeds_col_3, (const void**)fan_speeds_col_3_values, 4}, { set_fan_speed, fan_speeds_col_1, (const void**)fan_speeds_col_1_values, 2}};

View File

@@ -0,0 +1,99 @@
#include "current_printer.h"
#include "semaphore.h"
bool current_printer_move_printer(const char* axis, float amount, bool relative)
{
freeze_request_thread();
bool result = get_current_printer()->move_printer(axis, amount, relative);
unfreeze_request_thread();
return result;
}
bool current_printer_execute_feature(PrinterFeatures feature)
{
freeze_request_thread();
bool result = get_current_printer()->execute_feature(feature);
unfreeze_request_thread();
return result;
}
Macros current_printer_get_macros()
{
freeze_request_thread();
Macros macros = get_current_printer()->get_macros();
unfreeze_request_thread();
return macros;
}
int current_printer_get_macros_count()
{
freeze_request_thread();
int count = get_current_printer()->get_macros_count();
unfreeze_request_thread();
return count;
}
bool current_printer_execute_macro(const char* macro)
{
freeze_request_thread();
bool result = get_current_printer()->execute_macro(macro);
unfreeze_request_thread();
return result;
}
PowerDevices current_printer_get_power_devices()
{
freeze_request_thread();
PowerDevices power_devices = get_current_printer()->get_power_devices();
unfreeze_request_thread();
return power_devices;
}
int current_printer_get_power_devices_count()
{
freeze_request_thread();
int count = get_current_printer()->get_power_devices_count();
unfreeze_request_thread();
return count;
}
bool current_printer_set_power_device_state(const char* device_name, bool state)
{
freeze_request_thread();
bool result = get_current_printer()->set_power_device_state(device_name, state);
unfreeze_request_thread();
return result;
}
Files current_printer_get_files()
{
freeze_request_thread();
Files files = get_current_printer()->get_files();
unfreeze_request_thread();
return files;
}
bool current_printer_start_file(const char* filename)
{
freeze_request_thread();
bool result = get_current_printer()->start_file(filename);
unfreeze_request_thread();
return result;
}
Thumbnail current_printer_get_32_32_png_image_thumbnail(const char* gcode_filename)
{
freeze_request_thread();
Thumbnail thumbnail = get_current_printer()->get_32_32_png_image_thumbnail(gcode_filename);
unfreeze_request_thread();
return thumbnail;
}
bool current_printer_set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature)
{
freeze_request_thread();
bool result = get_current_printer()->set_target_temperature(device, temperature);
unfreeze_request_thread();
return result;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "printer_integration.hpp"
bool current_printer_move_printer(const char* axis, float amount, bool relative);
bool current_printer_execute_feature(PrinterFeatures feature);
Macros current_printer_get_macros();
int current_printer_get_macros_count();
bool current_printer_execute_macro(const char* macro);
PowerDevices current_printer_get_power_devices();
int current_printer_get_power_devices_count();
bool current_printer_set_power_device_state(const char* device_name, bool state);
Files current_printer_get_files();
bool current_printer_start_file(const char* filename);
Thumbnail current_printer_get_32_32_png_image_thumbnail(const char* gcode_filename);
bool current_printer_set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);

View File

@@ -1,182 +1,121 @@
#include "data_setup.h" #include "data_setup.h"
#include "lvgl.h" #include "semaphore.h"
#include "../conf/global_config.h" #include <esp_task_wdt.h>
#include <HTTPClient.h> #include <UrlEncode.h>
#include <ArduinoJson.h> #include "printer_integration.hpp"
#include "klipper/klipper_printer_integration.hpp"
#include "klipper-serial/serial_klipper_printer_integration.hpp"
#include "bambu/bambu_printer_integration.hpp"
#include "octoprint/octoprint_printer_integration.hpp"
const char *printer_state_messages[] = { const long data_update_interval = 780;
"Error",
"Idle",
"Printing"};
Printer printer = {0};
void send_gcode(bool wait, const char *gcode)
{
char buff[256] = {};
sprintf(buff, "http://%s:%d/printer/gcode/script?script=%s", global_config.klipperHost, global_config.klipperPort, gcode);
HTTPClient client;
client.begin(buff);
if (!wait)
{
client.setTimeout(1000);
}
try
{
client.GET();
}
catch (...)
{
Serial.println("Failed to send gcode");
}
}
char filename_buff[512] = {0};
void fetch_printer_data() void fetch_printer_data()
{ {
char buff[256] = {}; freeze_request_thread();
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; if (get_current_printer_data()->state == PrinterStateOffline)
client.begin(buff);
int httpCode = client.GET();
if (httpCode == 200)
{ {
String payload = client.getString(); if (!get_current_printer()->connect())
DynamicJsonDocument doc(4096);
deserializeJson(doc, payload);
auto status = doc["result"]["status"];
bool emit_state_update = false;
int printer_state = printer.state;
if (status.containsKey("webhooks"))
{ {
const char *state = status["webhooks"]["state"]; LOG_LN("Failed to connect to printer");
const char *message = status["webhooks"]["state_message"]; unfreeze_request_thread();
return;
if (strcmp(state, "ready") == 0 && printer.state == PRINTER_STATE_ERROR)
{
printer_state = PRINTER_STATE_IDLE;
}
else if (strcmp(state, "shutdown") == 0 && printer.state != PRINTER_STATE_ERROR)
{
printer_state = PRINTER_STATE_ERROR;
}
if (printer.state_message == NULL || strcmp(printer.state_message, message))
{
if (printer.state_message != NULL)
{
free(printer.state_message);
}
printer.state_message = (char *)malloc(strlen(message) + 1);
strcpy(printer.state_message, message);
emit_state_update = true;
}
}
if (printer_state != PRINTER_STATE_ERROR)
{
if (status.containsKey("extruder"))
{
printer.extruder_temp = status["extruder"]["temperature"];
printer.extruder_target_temp = status["extruder"]["target"];
bool can_extrude = status["extruder"]["can_extrude"];
printer.can_extrude = can_extrude == true;
}
if (status.containsKey("heater_bed"))
{
printer.bed_temp = status["heater_bed"]["temperature"];
printer.bed_target_temp = status["heater_bed"]["target"];
}
if (status.containsKey("toolhead"))
{
printer.position[0] = status["toolhead"]["position"][0];
printer.position[1] = status["toolhead"]["position"][1];
printer.position[2] = status["toolhead"]["position"][2];
const char *homed_axis = status["toolhead"]["homed_axes"];
printer.homed_axis = strcmp(homed_axis, "xyz") == 0;
}
if (status.containsKey("gcode_move"))
{
bool absolute_coords = status["gcode_move"]["absolute_coordinates"];
printer.absolute_coords = absolute_coords == true;
}
if (status.containsKey("virtual_sdcard"))
{
printer.print_progress = status["virtual_sdcard"]["progress"];
}
if (status.containsKey("print_stats"))
{
const char *filename = status["print_stats"]["filename"];
strcpy(filename_buff, filename);
printer.print_filename = filename_buff;
printer.elapsed_time_s = status["print_stats"]["print_duration"];
printer.filament_used_mm = status["print_stats"]["filament_used"];
const char *state = status["print_stats"]["state"];
if (strcmp(state, "printing") == 0)
{
printer_state = PRINTER_STATE_PRINTING;
}
else if (strcmp(state, "paused") == 0)
{
printer_state = PRINTER_STATE_PAUSED;
}
else if (strcmp(state, "complete") == 0 || strcmp(state, "cancelled") == 0 || strcmp(state, "standby") == 0)
{
printer_state = PRINTER_STATE_IDLE;
}
}
// TODO: make a call to /server/files/metadata to get more accurate time estimates
// https://moonraker.readthedocs.io/en/latest/web_api/#server-administration
if (printer.state == PRINTER_STATE_PRINTING && printer.print_progress > 0)
{
printer.remaining_time_s = (printer.elapsed_time_s / printer.print_progress) - printer.elapsed_time_s;
}
lv_msg_send(DATA_PRINTER_DATA, &printer);
}
if (printer.state != printer_state || emit_state_update)
{
printer.state = printer_state;
lv_msg_send(DATA_PRINTER_STATE, &printer);
} }
} }
else
bool fetch_result = get_current_printer()->fetch();
unfreeze_request_thread();
freeze_render_thread();
if (!fetch_result)
{ {
Serial.printf("Failed to fetch printer data: %d\n", httpCode); LOG_LN("Failed to fetch printer data")
get_current_printer()->disconnect();
} }
get_current_printer()->AnnouncePrinterData();
unfreeze_render_thread();
} }
long last_data_update = 0; void fetch_printer_data_minimal()
const long data_update_interval = 1500; {
PrinterDataMinimal data[PRINTER_CONFIG_COUNT] = {{}};
for (int i = 0; i < get_printer_count(); i++)
{
freeze_request_thread();
BasePrinter* printer = get_printer(i);
unfreeze_request_thread();
data[i] = printer->fetch_min();
}
freeze_render_thread();
announce_printer_data_minimal(data);
unfreeze_render_thread();
}
void data_loop() void data_loop()
{ {
if (millis() - last_data_update < data_update_interval) // Causes other threads that are trying to lock the thread to actually lock it
return; unfreeze_render_thread();
delay(1);
last_data_update = millis(); freeze_render_thread();
fetch_printer_data();
} }
void data_loop_background(void * param){
esp_task_wdt_init(10, true);
int loop_iter = 20;
while (true){
delay(data_update_interval);
fetch_printer_data();
if (global_config.multi_printer_mode) {
if (loop_iter++ > 20){
fetch_printer_data_minimal();
loop_iter = 0;
}
}
}
}
TaskHandle_t background_loop;
void data_setup() void data_setup()
{ {
printer.print_filename = filename_buff; BasePrinter** available_printers = (BasePrinter**)malloc(sizeof(BasePrinter*) * PRINTER_CONFIG_COUNT);
int count = 0;
int true_current_printer_index = 0;
for (int i = 0; i < PRINTER_CONFIG_COUNT; i++)
{
if (global_config.printer_config[i].setup_complete)
{
if (global_config.printer_index == i)
{
true_current_printer_index = count;;
}
switch (global_config.printer_config[i].printer_type)
{
case PrinterType::PrinterTypeKlipper:
available_printers[count++] = new KlipperPrinter(i);
break;
case PrinterType::PrinterTypeBambuLocal:
available_printers[count++] = new BambuPrinter(i);
break;
case PrinterType::PrinterTypeKlipperSerial:
available_printers[count++] = new SerialKlipperPrinter(i);
break;
case PrinterType::PrinterTypeOctoprint:
available_printers[count++] = new OctoPrinter(i);
break;
}
}
}
initialize_printers(available_printers, count);
set_current_printer(true_current_printer_index);
LOG_F(("Free heap after printer creation: %d bytes\n", esp_get_free_heap_size()));
semaphore_init();
fetch_printer_data(); fetch_printer_data();
} freeze_render_thread();
xTaskCreatePinnedToCore(data_loop_background, "data_loop_background", 5000, NULL, 2, &background_loop, 0);
}

View File

@@ -1,37 +1,4 @@
#pragma once #pragma once
enum {
PRINTER_STATE_ERROR = 0,
PRINTER_STATE_IDLE = 1,
PRINTER_STATE_PRINTING = 2,
PRINTER_STATE_PAUSED = 3,
};
extern const char* printer_state_messages[];
typedef struct _Printer {
unsigned char state;
char* state_message;
float extruder_temp;
float extruder_target_temp;
float bed_temp;
float bed_target_temp;
float position[3];
unsigned char can_extrude;
unsigned char homed_axis;
unsigned char absolute_coords;
float elapsed_time_s;
float remaining_time_s;
float filament_used_mm;
char* print_filename; // 0 -> 1
float print_progress;
} Printer;
extern Printer printer;
#define DATA_PRINTER_STATE 1
#define DATA_PRINTER_DATA 2
void data_loop(); void data_loop();
void data_setup(); void data_setup();
void send_gcode(bool wait, const char* gcode);

View File

@@ -0,0 +1,120 @@
#ifdef CYD_SCREEN_DRIVER_ESP32_2432S028R
#include "../screen_driver.h"
#ifdef CYD_SCREEN_VERTICAL
#error "Vertical screen not supported with the ESP32_2432S028R driver"
#endif
#include <SPI.h>
#include <TFT_eSPI.h>
#include "../../conf/global_config.h"
#include "lvgl.h"
#include <XPT2046_Touchscreen.h>
#include <TFT_eSPI.h>
#include "../lv_setup.h"
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
#define CPU_FREQ_HIGH 240
#define CPU_FREQ_LOW 80
SPIClass touchscreen_spi = SPIClass(HSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10];
TFT_eSPI tft = TFT_eSPI();
void screen_setBrightness(byte brightness)
{
// calculate duty, 4095 from 2 ^ 12 - 1
uint32_t duty = (4095 / 255) * brightness;
// write duty to LEDC
ledcWrite(0, duty);
}
void screen_lv_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
if (touchscreen.tirqTouched() && touchscreen.touched())
{
TS_Point p = touchscreen.getPoint();
data->state = LV_INDEV_STATE_PR;
data->point.x = p.x;
data->point.y = p.y;
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
void set_invert_display(){
tft.invertDisplay(global_config.printer_config[global_config.printer_index].invert_colors);
}
void screen_setup()
{
touchscreen_spi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreen_spi);
touchscreen.setRotation(global_config.rotate_screen ? 3 : 1);
lv_init();
tft.init();
if (global_config.display_mode) {
// <3 https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display/blob/main/cyd.md#the-display-doesnt-look-as-good
tft.writecommand(ILI9341_GAMMASET); //Gamma curve selected
tft.writedata(2);
delay(120);
tft.writecommand(ILI9341_GAMMASET); //Gamma curve selected
tft.writedata(1);
}
ledcSetup(0, 5000, 12);
ledcAttachPin(21, 0);
tft.setRotation(global_config.rotate_screen ? 3 : 1);
tft.fillScreen(TFT_BLACK);
set_invert_display();
touchscreen_spi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreen_spi);
lv_disp_draw_buf_init(&draw_buf, buf, NULL, CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = CYD_SCREEN_WIDTH_PX;
disp_drv.ver_res = CYD_SCREEN_HEIGHT_PX;
disp_drv.flush_cb = screen_lv_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = screen_lv_touchRead;
lv_indev_drv_register(&indev_drv);
}
#endif // CYD_SCREEN_DRIVER_ESP32_2432S028R

View File

@@ -0,0 +1,154 @@
#ifdef CYD_SCREEN_DRIVER_ESP32_3248S035C
#include "../screen_driver.h"
#include "lvgl.h"
#include <TAMC_GT911.h>
#include <TFT_eSPI.h>
#include "../../conf/global_config.h"
#include "../lv_setup.h"
#define TOUCH_SDA 33
#define TOUCH_SCL 32
#define TOUCH_INT 21
#define TOUCH_RST 25
#define TOUCH_WIDTH 320
#define TOUCH_HEIGHT 480
#define LED_PIN_R 4
#define LED_PIN_G 16
#define LED_PIN_B 17
TAMC_GT911 tp = TAMC_GT911(TOUCH_SDA, TOUCH_SCL, TOUCH_INT, TOUCH_RST, TOUCH_WIDTH, TOUCH_HEIGHT);
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10];
TFT_eSPI tft = TFT_eSPI();
void screen_setBrightness(byte brightness)
{
// calculate duty, 4095 from 2 ^ 12 - 1
uint32_t duty = (4095 / 255) * brightness;
// write duty to LEDC
ledcWrite(0, duty);
}
void screen_lv_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
tp.read();
if (!tp.isTouched)
{
data->state = LV_INDEV_STATE_REL;
}
else
{
data->state = LV_INDEV_STATE_PR;
for (int i = 0; i < tp.touches; i++)
{
uint16_t magicX; // fix GT911 driver - orientation and handle rotation
uint16_t magicY;
if (!global_config.rotate_screen)
{
magicY = tp.points[i].x;
magicX = TOUCH_HEIGHT - tp.points[i].y;
}
else
{
#ifdef CYD_SCREEN_VERTICAL
// I don't even want to know why this works...
magicY = TOUCH_HEIGHT - tp.points[i].x;
magicX = tp.points[i].y - 160;
#else
magicY = TOUCH_WIDTH - tp.points[i].x;
magicX = tp.points[i].y;
#endif
}
data->point.x = magicX;
data->point.y = magicY;
}
}
}
void set_invert_display()
{
tft.invertDisplay(global_config.printer_config[global_config.printer_index].invert_colors);
}
void set_LED_color(uint8_t rgbVal[3])
{
analogWrite(LED_PIN_R, 255 - rgbVal[0]);
analogWrite(LED_PIN_G, 255 - rgbVal[1]);
analogWrite(LED_PIN_B, 255 - rgbVal[2]);
}
void LED_init()
{
pinMode(LED_PIN_R, OUTPUT);
pinMode(LED_PIN_G, OUTPUT);
pinMode(LED_PIN_B, OUTPUT);
uint8_t rgbVal[3] = {0, 0, 0};
set_LED_color(rgbVal);
}
void screen_setup()
{
// Initialize the touchscreen
tp.begin();
// Initialize LVGL
lv_init();
// Initialize the display
tft.init();
ledcSetup(0, 5000, 12);
ledcAttachPin(TFT_BL, 0);
tft.fillScreen(TFT_BLACK);
set_invert_display();
LED_init();
lv_disp_draw_buf_init(&draw_buf, buf, NULL, TFT_WIDTH * TFT_HEIGHT / 10);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
#ifdef CYD_SCREEN_VERTICAL
disp_drv.hor_res = TFT_WIDTH;
disp_drv.ver_res = TFT_HEIGHT;
tp.setRotation(2);
tft.setRotation(global_config.rotate_screen ? 2 : 0);
#else
disp_drv.hor_res = TFT_HEIGHT;
disp_drv.ver_res = TFT_WIDTH;
tft.setRotation(global_config.rotate_screen ? 3 : 1);
tp.setRotation(ROTATION_NORMAL);
#endif
disp_drv.flush_cb = screen_lv_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
// Initialize the (dummy) input device driver
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = screen_lv_touchRead;
lv_indev_drv_register(&indev_drv);
}
#endif // CYD_SCREEN_DRIVER_ESP32_3248S035C

View File

@@ -0,0 +1,183 @@
// Adapted from https://github.com/OzInFl/Elecrow-3.5-RGB-TFT-SQUARELINE-EXAMPLE
#ifdef CYD_SCREEN_DRIVER_ESP32_CROWPANEL_35C
#include "../screen_driver.h"
#include "lvgl.h"
#include "../../conf/global_config.h"
#include <LovyanGFX.hpp>
#include <Arduino.h>
#include <Wire.h>
#ifdef CYD_SCREEN_VERTICAL
#error "Vertical screen not supported with the ESP32_CROWPANEL_28R driver"
#endif
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10];
#define BUZZER_PIN 20
#define LCD_BL 46
#define SDA_FT6236 38
#define SCL_FT6236 39
#define I2C_TOUCH_ADDR 0x38
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ILI9488 _panel_instance;
lgfx::Bus_Parallel16 _bus_instance;
lgfx::Touch_FT5x06 _touch_instance;
public:
LGFX()
{
auto bus_cfg = _bus_instance.config();
bus_cfg.port = 0;
bus_cfg.freq_write = 80000000;
bus_cfg.pin_wr = 18;
bus_cfg.pin_rd = 48;
bus_cfg.pin_rs = 45;
bus_cfg.pin_d0 = 47;
bus_cfg.pin_d1 = 21;
bus_cfg.pin_d2 = 14;
bus_cfg.pin_d3 = 13;
bus_cfg.pin_d4 = 12;
bus_cfg.pin_d5 = 11;
bus_cfg.pin_d6 = 10;
bus_cfg.pin_d7 = 9;
bus_cfg.pin_d8 = 3;
bus_cfg.pin_d9 = 8;
bus_cfg.pin_d10 = 16;
bus_cfg.pin_d11 = 15;
bus_cfg.pin_d12 = 7;
bus_cfg.pin_d13 = 6;
bus_cfg.pin_d14 = 5;
bus_cfg.pin_d15 = 4;
_bus_instance.config(bus_cfg);
_panel_instance.setBus(&_bus_instance);
auto panel_cfg = _panel_instance.config();
panel_cfg.pin_cs = -1;
panel_cfg.pin_rst = -1;
panel_cfg.pin_busy = -1;
panel_cfg.memory_width = 320;
panel_cfg.memory_height = 480;
panel_cfg.panel_width = 320;
panel_cfg.panel_height = 480;
panel_cfg.offset_x = 0;
panel_cfg.offset_y = 0;
panel_cfg.offset_rotation = 0;
panel_cfg.dummy_read_pixel = 8;
panel_cfg.dummy_read_bits = 1;
panel_cfg.readable = true;
panel_cfg.invert = global_config.printer_config[global_config.printer_index].invert_colors ? true : false;
panel_cfg.rgb_order = false;
panel_cfg.dlen_16bit = true;
panel_cfg.bus_shared = true;
_panel_instance.config(panel_cfg);
auto touch_cfg = _touch_instance.config();
touch_cfg.x_min = 0;
touch_cfg.x_max = 319;
touch_cfg.y_min = 0;
touch_cfg.y_max = 479;
touch_cfg.pin_int = -1;
touch_cfg.bus_shared = false;
touch_cfg.offset_rotation = 0;
touch_cfg.i2c_port = 1;
touch_cfg.i2c_addr = 0x38;
touch_cfg.pin_sda = 38;
touch_cfg.pin_scl = 39;
touch_cfg.freq = 400000;
_touch_instance.config(touch_cfg);
_panel_instance.setTouch(&_touch_instance);
setPanel(&_panel_instance);
}
};
LGFX tft;
void screen_setBrightness(unsigned char brightness)
{
// TODO
}
void set_invert_display()
{
// TODO
}
void screen_lv_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.writePixels((lgfx::rgb565_t *)&color_p->full, w * h);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
uint16_t touchX, touchY;
bool touched = tft.getTouch(&touchX, &touchY);
if (touchX > CYD_SCREEN_WIDTH_PX || touchY > CYD_SCREEN_HEIGHT_PX)
{
LOG_LN("Y or y outside of expected parameters..");
}
else
{
data->state = touched ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
data->point.x = touchX;
data->point.y = touchY;
}
}
void screen_setup()
{
pinMode(BUZZER_PIN, OUTPUT);
ledcSetup(4, 5000, 8);
ledcAttachPin(BUZZER_PIN, 4);
tft.begin();
tft.setRotation(global_config.rotate_screen ? 3 : 1);
delay(500);
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);
/*
ledcSetup(0, 5000, 12);
ledcAttachPin(LCD_BL, 0);
*/
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf, NULL, CYD_SCREEN_WIDTH_PX * CYD_SCREEN_HEIGHT_PX / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = CYD_SCREEN_WIDTH_PX;
disp_drv.ver_res = CYD_SCREEN_HEIGHT_PX;
disp_drv.flush_cb = screen_lv_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = screen_lv_touchRead;
lv_indev_drv_register(&indev_drv);
}
#endif

View File

@@ -0,0 +1,100 @@
#ifdef CYD_SCREEN_DRIVER_ESP32_CROWPANEL_28R
#include "../screen_driver.h"
#ifdef CYD_SCREEN_VERTICAL
#error "Vertical screen not supported with the ESP32_CROWPANEL_28R driver"
#endif
#include <SPI.h>
#include <TFT_eSPI.h>
#include "../../conf/global_config.h"
#include "lvgl.h"
#include <TFT_eSPI.h>
#include "../lv_setup.h"
#define TOUCH_THRESHOLD 600
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10];
TFT_eSPI tft = TFT_eSPI();
uint16_t touchX, touchY;
void screen_setBrightness(byte brightness)
{
// calculate duty, 4095 from 2 ^ 12 - 1
uint32_t duty = (4095 / 255) * brightness;
// write duty to LEDC
ledcWrite(0, duty);
}
void screen_lv_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
if (tft.getTouch( &touchX, &touchY, TOUCH_THRESHOLD))
{
data->state = LV_INDEV_STATE_PR;
data->point.x = touchX;
data->point.y = touchY;
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
void set_invert_display(){
tft.invertDisplay(global_config.printer_config[global_config.printer_index].invert_colors);
}
void screen_setup()
{
uint16_t calData[5] = { 189, 3416, 359, 3439, 1};
lv_init();
tft.init();
tft.fillScreen(TFT_BLACK);
tft.invertDisplay(false);
delay(300);
tft.setRotation(1);
tft.setTouch( calData );
ledcSetup(0, 5000, 12);
ledcAttachPin(TFT_BL, 0);
lv_disp_draw_buf_init(&draw_buf, buf, NULL, CYD_SCREEN_HEIGHT_PX * CYD_SCREEN_WIDTH_PX / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = CYD_SCREEN_WIDTH_PX;
disp_drv.ver_res = CYD_SCREEN_HEIGHT_PX;
disp_drv.flush_cb = screen_lv_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = screen_lv_touchRead;
lv_indev_drv_register(&indev_drv);
}
#endif // CYD_SCREEN_DRIVER_ESP32_CROWPANEL_28R

View File

@@ -0,0 +1,67 @@
#ifdef CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY
#include "../screen_driver.h"
#include <esp32_smartdisplay.h>
#include "../../conf/global_config.h"
#include "lvgl.h"
#include "../lv_setup.h"
typedef void (*lv_disp_drv_t_flush_cb)(_lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
static lv_disp_drv_t_flush_cb original_screen_driver;
void screen_setBrightness(byte brightness)
{
smartdisplay_lcd_set_backlight(brightness / 255.0f);
}
void set_invert_display()
{
lv_obj_invalidate(lv_scr_act());
}
void lv_screen_intercept(_lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
if (global_config.printer_config[global_config.printer_index].invert_colors) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
for (int i = 0 ; i < w * h; i++){
color_p[i].full ^= 0xFFFF;
}
}
original_screen_driver(disp_drv, area, color_p);
}
#ifndef ROTATION_INVERTED
#ifdef CYD_SCREEN_VERTICAL
#define ROTATION_INVERTED LV_DISP_ROT_180
#else
#define ROTATION_INVERTED LV_DISP_ROT_270
#endif
#endif
#ifndef ROTATION_NORMAL
#ifdef CYD_SCREEN_VERTICAL
#define ROTATION_NORMAL LV_DISP_ROT_NONE
#else
#define ROTATION_NORMAL LV_DISP_ROT_90
#endif
#endif
void screen_setup()
{
smartdisplay_init();
#ifndef CYD_SCREEN_DISABLE_INVERT_COLORS
if (original_screen_driver == NULL){
original_screen_driver = lv_disp_get_default()->driver->flush_cb;
lv_disp_get_default()->driver->flush_cb = lv_screen_intercept;
}
#endif // CYD_SCREEN_DISABLE_INVERT_COLORS
lv_disp_set_rotation(lv_disp_get_default(), (global_config.rotate_screen) ? ROTATION_INVERTED : ROTATION_NORMAL);
}
#endif // CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY

View File

@@ -1,63 +0,0 @@
#include <list>
#include "files_query.h"
#include "../conf/global_config.h"
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <HardwareSerial.h>
// Always has +1 entry with a null'd name
FILESYSTEM_FILE* last_query = NULL;
FILESYSTEM_FILE* get_files(){
if (last_query != NULL){
FILESYSTEM_FILE* current = last_query;
while (current->name != NULL){
free(current->name);
current += 1;
}
free(last_query);
}
std::list<FILESYSTEM_FILE> files;
char buff[256] = {};
sprintf(buff, "http://%s:%d/server/files/list", global_config.klipperHost, global_config.klipperPort);
HTTPClient client;
client.begin(buff);
int httpCode = client.GET();
int count = 0;
if (httpCode == 200){
String payload = client.getString();
DynamicJsonDocument doc(60000);
auto a = deserializeJson(doc, payload);
Serial.printf("JSON PARSE: %s\n", a.c_str());
auto result = doc["result"].as<JsonArray>();
for (auto file : result){
FILESYSTEM_FILE f = {0};
const char* path = file["path"];
f.name = (char*)malloc(strlen(path) + 1);
strcpy(f.name, path);
f.modified = file["modified"];
files.push_back(f);
count++;
}
}
//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);
last_query = result;
result[files.size()].name = NULL;
for (auto file : files){
*result = file;
result += 1;
}
return last_query;
}

View File

@@ -1,22 +0,0 @@
/*
At some point it may be a fun challenge to try to implement a virtual folder structure, but not today.
typedef struct _FILESYSTEM_FILE {
char* name;
char* parent_folder_name;
long level;
} FILESYSTEM_FILE;
typedef struct _FILESYSTEM_FOLDER {
char** files;
char* folder_path;
FILESYSTEM_FOLDER* folders;
} FILESYSTEM_FOLDER;
*/
typedef struct _FILESYSTEM_FILE {
char* name;
float modified;
} FILESYSTEM_FILE;
FILESYSTEM_FILE* get_files();

View File

@@ -0,0 +1,399 @@
#include "serial_klipper_printer_integration.hpp"
#include <HardwareSerial.h>
#include <UrlEncode.h>
#include "../../ui/serial/serial_console.h"
void clear_serial_buffer(bool can_rely_on_newline_terminator = true)
{
if (can_rely_on_newline_terminator)
{
if (Serial.available())
{
while (Serial.read() != '\n')
;
}
}
else
{
while (Serial.available())
{
Serial.read();
}
}
}
// Request: {timeout} {method} {endpoint}
// Response: {status code} {body}
bool make_serial_request(JsonDocument &out, int timeout_ms, HttpRequestType requestType, const char* endpoint)
{
serial_console::global_disable_serial_console = true;
temporary_config.debug = false;
char buff[10];
clear_serial_buffer();
// TODO: Add semaphore here
if (!Serial.availableForWrite())
{
return false;
}
Serial.printf("HTTP_REQUEST %d %s %s\n", timeout_ms, requestType == HttpGet ? "GET" : "POST", endpoint);
if (timeout_ms <= 0)
{
return true;
}
unsigned long _m = millis();
while (!Serial.available() && millis() < _m + timeout_ms + 10) delay(1);
if (!Serial.available())
{
Serial.println("Timeout...");
return false;
}
Serial.readBytes(buff, 4);
buff[3] = 0;
if (buff[0] < '0' || buff[0] > '9')
{
Serial.printf("Invalid error code, got char '%c'\n", buff[0]);
clear_serial_buffer();
return false;
}
int status_code = atoi(buff);
if (status_code < 200 || status_code >= 300)
{
Serial.println("Non-200 error code");
clear_serial_buffer();
return false;
}
auto result = deserializeJson(out, Serial);
Serial.printf("Deserialization result: %s\n", result.c_str());
bool success = result == DeserializationError::Ok;
return success;
}
typedef struct
{
int len;
unsigned char* data;
} BinaryResponse;
// Request: {timeout} {method} {endpoint}
// Response: {8 char 0's padded body length}{body}
bool make_binary_request(BinaryResponse* data, int timeout_ms, HttpRequestType requestType, const char* endpoint)
{
serial_console::global_disable_serial_console = true;
temporary_config.debug = false;
char buff[10];
clear_serial_buffer();
// TODO: Add semaphore here
if (!Serial.availableForWrite() || timeout_ms <= 0)
{
return false;
}
Serial.printf("HTTP_BINARY %d %s %s\n", timeout_ms, requestType == HttpGet ? "GET" : "POST", endpoint);
unsigned long _m = millis();
while (!Serial.available() && millis() < _m + timeout_ms + 10) delay(1);
if (!Serial.available())
{
Serial.println("Timeout...");
return false;
}
Serial.readBytes(buff, 8);
buff[9] = 0;
if (buff[0] < '0' || buff[0] > '9')
{
Serial.println("Invalid length");
clear_serial_buffer(false);
return false;
}
int data_length = atoi(buff);
if (data_length <= 0)
{
Serial.println("0 Length");
clear_serial_buffer(false);
return false;
}
data->len = data_length;
data->data = (unsigned char*)malloc(data_length);
if (data->data == NULL)
{
Serial.println("Failed to allocate memory");
clear_serial_buffer(false);
return false;
}
bool result = Serial.readBytes((char*)data->data, data_length) == data_length;
if (!result)
{
free(data->data);
}
return result;
}
bool make_serial_request_nocontent(HttpRequestType requestType, const char* endpoint)
{
JsonDocument doc;
make_serial_request(doc, 0, requestType, endpoint);
return true;
}
bool SerialKlipperPrinter::connect()
{
return connection_test_serial_klipper(printer_config) == KlipperConnectionStatus::ConnectOk;
}
bool SerialKlipperPrinter::fetch()
{
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks&fan&display_status"))
{
if (printer_data.state == PrinterStateOffline)
{
printer_data.state = PrinterStateError;
}
klipper_request_consecutive_fail_count = 0;
parse_state(doc);
}
else
{
klipper_request_consecutive_fail_count++;
if (klipper_request_consecutive_fail_count >= 5)
{
printer_data.state = PrinterStateOffline;
return false;
}
}
return true;
}
PrinterDataMinimal SerialKlipperPrinter::fetch_min()
{
JsonDocument doc;
PrinterDataMinimal data = {};
data.success = false;
if (!printer_config->setup_complete)
{
data.state = PrinterStateOffline;
return data;
}
data.success = true;
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?webhooks&print_stats&virtual_sdcard"))
{
data.state = PrinterState::PrinterStateIdle;
parse_state_min(doc, &data);
data.power_devices = get_power_devices_count();
}
else
{
data.state = PrinterState::PrinterStateOffline;
data.power_devices = get_power_devices_count();
}
return data;
}
Macros SerialKlipperPrinter::get_macros()
{
Macros macros = {0};
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help"))
{
macros = parse_macros(doc);
}
return macros;
}
int SerialKlipperPrinter::get_macros_count()
{
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help"))
{
return parse_macros_count(doc);
}
return 0;
}
PowerDevices SerialKlipperPrinter::get_power_devices()
{
PowerDevices power_devices = {0};
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices"))
{
power_devices = parse_power_devices(doc);
}
return power_devices;
}
int SerialKlipperPrinter::get_power_devices_count()
{
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices"))
{
return parse_power_devices_count(doc);
}
return 0;
}
bool SerialKlipperPrinter::set_power_device_state(const char* device_name, bool state)
{
String request = "/machine/device_power/device?device=" + urlEncode(device_name) + "&action=" + (state ? "on" : "off");
return make_serial_request_nocontent(HttpGet, request.c_str());
}
Files SerialKlipperPrinter::get_files()
{
Files files_result = {0};
files_result.success = false;
JsonDocument doc;
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
std::list<KlipperFileSystemFile> files;
auto timer_request = millis();
bool result = make_serial_request(doc, 5000, HttpGet, "/server/files/list");
auto timer_parse = millis();
if (!result)
{
return files_result;
}
parse_file_list(doc, files, 20);
files_result.available_files = (char**)malloc(sizeof(char*) * files.size());
if (files_result.available_files == NULL){
LOG_LN("Failed to allocate memory");
for (auto file : files){
free(file.name);
}
return files_result;
}
for (auto file : files){
files_result.available_files[files_result.count++] = file.name;
}
files_result.success = true;
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))
return files_result;
}
bool SerialKlipperPrinter::start_file(const char* filename)
{
JsonDocument doc;
String request = "/printer/print/start?filename=" + urlEncode(filename);
return make_serial_request_nocontent(HttpPost, request.c_str());;
}
Thumbnail SerialKlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
{
Thumbnail thumbnail = {0};
JsonDocument doc;
char* img_filename_path = NULL;
String request = "/server/files/thumbnails?filename=" + urlEncode(gcode_filename);
if (make_serial_request(doc, 1000, HttpGet, request.c_str()))
{
img_filename_path = parse_thumbnails(doc);
doc.clear();
}
if (img_filename_path == NULL)
{
return thumbnail;
}
request = "/server/files/gcodes/" + urlEncode(img_filename_path);
BinaryResponse data = {0};
if (make_binary_request(&data, 2000, HttpGet, request.c_str()))
{
thumbnail.png = data.data;
thumbnail.size = data.len;
thumbnail.success = true;
}
free(img_filename_path);
return thumbnail;
}
bool SerialKlipperPrinter::send_gcode(const char* gcode, bool wait)
{
JsonDocument doc;
String request = "/printer/gcode/script?script=" + urlEncode(gcode);
return wait
? make_serial_request(doc, 5000, HttpGet, request.c_str())
: make_serial_request_nocontent(HttpGet, request.c_str());
}
bool SerialKlipperPrinter::send_emergency_stop()
{
return make_serial_request_nocontent(HttpGet, "/printer/emergency_stop");
}
int SerialKlipperPrinter::get_slicer_time_estimate_s()
{
if (printer_data.state != PrinterStatePrinting && printer_data.state != PrinterStatePaused)
return 0;
String request = "/server/files/metadata?filename=" + urlEncode(printer_data.print_filename);
JsonDocument doc;
if (!make_serial_request(doc, 2000, HttpGet, request.c_str()))
{
return 0;
}
return parse_slicer_time_estimate(doc);
}
KlipperConnectionStatus connection_test_serial_klipper(PrinterConfiguration* config)
{
serial_console::global_disable_serial_console = true;
temporary_config.debug = false;
JsonDocument doc;
if (make_serial_request(doc, 1000, HttpGet, "/printer/info"))
{
return KlipperConnectionStatus::ConnectOk;
}
return KlipperConnectionStatus::ConnectFail;
}

View File

@@ -0,0 +1,26 @@
#include "../klipper/klipper_printer_integration.hpp"
class SerialKlipperPrinter : public KlipperPrinter
{
protected:
bool send_emergency_stop();
int get_slicer_time_estimate_s();
public:
SerialKlipperPrinter(int index) : KlipperPrinter(index)
{}
bool connect();
bool fetch();
PrinterDataMinimal fetch_min();
Macros get_macros();
int get_macros_count();
PowerDevices get_power_devices();
int get_power_devices_count();
bool set_power_device_state(const char* device_name, bool state);
Files get_files();
bool start_file(const char* filename);
Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
bool send_gcode(const char* gcode, bool wait = true);
};
KlipperConnectionStatus connection_test_serial_klipper(PrinterConfiguration* config);

View File

@@ -0,0 +1,519 @@
#include "klipper_printer_integration.hpp"
#include "../../conf/global_config.h"
#include <HTTPClient.h>
#include <UrlEncode.h>
#include <ArduinoJson.h>
#include <list>
void KlipperPrinter::configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout)
{
client.useHTTP10(stream);
if (timeout > 0){
client.setTimeout(timeout);
client.setConnectTimeout(timeout);
}
client.begin("http://" + String(printer_config->printer_host) + ":" + String(printer_config->klipper_port) + url_part);
if (printer_config->auth_configured) {
client.addHeader("X-Api-Key", printer_config->printer_auth);
}
}
int KlipperPrinter::get_slicer_time_estimate_s()
{
if (printer_data.state != PrinterStatePrinting && printer_data.state != PrinterStatePaused)
return 0;
HTTPClient client;
configure_http_client(client, "/server/files/metadata?filename=" + urlEncode(printer_data.print_filename), true, 5000);
int http_code = client.GET();
if (http_code != 200)
return 0;
JsonDocument doc;
deserializeJson(doc, client.getStream());
return parse_slicer_time_estimate(doc);
}
bool KlipperPrinter::send_gcode(const char *gcode, bool wait)
{
HTTPClient client;
configure_http_client(client, "/printer/gcode/script?script=" + urlEncode(gcode), false, wait ? 5000 : 750);
LOG_F(("Sending gcode: %s\n", gcode))
try
{
client.GET();
return true;
}
catch (...)
{
LOG_LN("Failed to send gcode");
return false;
}
}
bool KlipperPrinter::move_printer(const char* axis, float amount, bool relative)
{
if (!printer_data.homed_axis || printer_data.state == PrinterStatePrinting)
return false;
char gcode[64];
const char* extra = (amount > 0) ? "+" : "";
const char* start = "";
const char* end = "";
if (printer_data.absolute_coords && relative) {
start = "G91\n";
}
else if (!printer_data.absolute_coords && !relative) {
start = "G90\n";
}
if (printer_data.absolute_coords && relative) {
end = "\nG90";
}
else if (!printer_data.absolute_coords && !relative) {
end = "\nG91";
}
sprintf(gcode, "%sG1 %s%s%.3f F6000%s", start, axis, extra, amount, end);
send_gcode(gcode);
lock_absolute_relative_mode_swap = 2;
return true;
}
bool KlipperPrinter::send_emergency_stop()
{
HTTPClient client;
configure_http_client(client, "/printer/emergency_stop", false, 5000);
try
{
return client.GET() == 200;
}
catch (...)
{
LOG_LN("Failed to send estop");
return false;
}
}
bool KlipperPrinter::execute_feature(PrinterFeatures feature)
{
switch (feature)
{
case PrinterFeatureRestart:
return send_gcode("RESTART", false);
case PrinterFeatureFirmwareRestart:
return send_gcode("FIRMWARE_RESTART", false);
case PrinterFeatureHome:
return send_gcode("G28");
case PrinterFeatureDisableSteppers:
return send_gcode("M18");
case PrinterFeaturePause:
return send_gcode("PAUSE");
case PrinterFeatureResume:
return send_gcode("RESUME");
case PrinterFeatureStop:
return send_gcode("CANCEL_PRINT");
case PrinterFeatureEmergencyStop:
LOG_LN("Sending estop");
send_gcode("M112", false);
send_emergency_stop();
return true;
case PrinterFeatureExtrude:
if (printer_data.state == PrinterStatePrinting)
{
return false;
}
if (printer_config->custom_filament_move_macros)
{
return send_gcode("FILAMENT_EXTRUDE");
}
else
{
return send_gcode("M83\nG1 E25 F300");
}
case PrinterFeatureRetract:
if (printer_data.state == PrinterStatePrinting)
{
return false;
}
if (printer_config->custom_filament_move_macros)
{
return send_gcode("FILAMENT_RETRACT");
}
else
{
return send_gcode("M83\nG1 E-25 F300");
}
case PrinterFeatureCooldown:
return send_gcode("M104 S0\nM140 S0");
default:
LOG_F(("Unsupported printer feature %d", feature));
}
return false;
}
bool KlipperPrinter::connect()
{
return connection_test_klipper(printer_config) == KlipperConnectionStatus::ConnectOk;
}
bool KlipperPrinter::fetch()
{
HTTPClient client;
configure_http_client(client, "/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks&fan&display_status", true, 1000);
int http_code = client.GET();
if (http_code == 200)
{
if (printer_data.state == PrinterStateOffline)
{
printer_data.state = PrinterStateError;
}
klipper_request_consecutive_fail_count = 0;
JsonDocument doc;
deserializeJson(doc, client.getStream());
parse_state(doc);
}
else
{
klipper_request_consecutive_fail_count++;
LOG_F(("Failed to fetch printer data: %d\n", http_code));
if (klipper_request_consecutive_fail_count >= 5)
{
printer_data.state = PrinterStateOffline;
return false;
}
}
return true;
}
PrinterDataMinimal KlipperPrinter::fetch_min()
{
PrinterDataMinimal data = {};
data.success = false;
if (!printer_config->setup_complete)
{
data.state = PrinterStateOffline;
return data;
}
data.success = true;
HTTPClient client;
configure_http_client(client, "/printer/objects/query?webhooks&print_stats&virtual_sdcard", true, 1000);
int http_code = client.GET();
if (http_code == 200)
{
data.state = PrinterState::PrinterStateIdle;
data.power_devices = get_power_devices_count();
JsonDocument doc;
deserializeJson(doc, client.getStream());
parse_state_min(doc, &data);
}
else
{
data.state = PrinterState::PrinterStateOffline;
data.power_devices = get_power_devices_count();
}
return data;
}
void KlipperPrinter::disconnect()
{
// Nothing to disconnect, everything is http request based
printer_data.state = PrinterStateOffline;
}
Macros KlipperPrinter::get_macros()
{
HTTPClient client;
Macros macros = {0};
configure_http_client(client, "/printer/gcode/help", true, 1000);
int http_code = client.GET();
if (http_code == 200){
JsonDocument doc;
deserializeJson(doc, client.getStream());
return parse_macros(doc);
}
return macros;
}
int KlipperPrinter::get_macros_count()
{
HTTPClient client;
configure_http_client(client, "/printer/gcode/help", true, 1000);
int http_code = client.GET();
if (http_code == 200){
JsonDocument doc;
deserializeJson(doc, client.getStream());
return parse_macros_count(doc);
}
else {
return 0;
}
}
bool KlipperPrinter::execute_macro(const char* macro)
{
return send_gcode(macro);
}
PowerDevices KlipperPrinter::get_power_devices()
{
HTTPClient client;
PowerDevices power_devices = {0};
configure_http_client(client, "/machine/device_power/devices", true, 1000);
int http_code = client.GET();
if (http_code == 200){
JsonDocument doc;
deserializeJson(doc, client.getStream());
return parse_power_devices(doc);
}
return power_devices;
}
int KlipperPrinter::get_power_devices_count()
{
HTTPClient client;
configure_http_client(client, "/machine/device_power/devices", true, 1000);
int http_code = client.GET();
if (http_code == 200){
JsonDocument doc;
deserializeJson(doc, client.getStream());
return parse_power_devices_count(doc);
}
else {
return 0;
}
}
bool KlipperPrinter::set_power_device_state(const char* device_name, bool state)
{
HTTPClient client;
configure_http_client(client, "/machine/device_power/device?device=" + urlEncode(device_name) + "&action=" + (state ? "on" : "off"), true, 1000);
return client.POST("") == 200;
}
#define KLIPPER_FILE_FETCH_LIMIT 20
Files KlipperPrinter::get_files()
{
Files files_result = {0};
HTTPClient client;
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
std::list<KlipperFileSystemFile> files;
auto timer_request = millis();
configure_http_client(client, "/server/files/list", true, 5000);
int http_code = client.GET();
auto timer_parse = millis();
if (http_code == 200)
{
JsonDocument doc;
auto parseResult = deserializeJson(doc, client.getStream());
LOG_F(("Json parse: %s\n", parseResult.c_str()))
parse_file_list(doc, files, KLIPPER_FILE_FETCH_LIMIT);
}
else
{
return files_result;
}
files_result.available_files = (char**)malloc(sizeof(char*) * files.size());
if (files_result.available_files == NULL){
LOG_LN("Failed to allocate memory");
for (auto file : files){
free(file.name);
}
return files_result;
}
for (auto file : files){
files_result.available_files[files_result.count++] = file.name;
}
files_result.success = true;
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))
return files_result;
}
bool KlipperPrinter::start_file(const char *filename)
{
HTTPClient client;
configure_http_client(client, "/printer/print/start?filename=" + urlEncode(filename), false, 1000);
int http_code = client.POST("");
LOG_F(("Print start: HTTP %d\n", http_code))
return http_code == 200;
}
bool KlipperPrinter::set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature)
{
char gcode[64] = {0};
switch (device)
{
case PrinterTemperatureDeviceBed:
sprintf(gcode, "M140 S%d", temperature);
break;
case PrinterTemperatureDeviceNozzle1:
sprintf(gcode, "M104 S%d", temperature);
break;
default:
LOG_F(("Unknown temperature device %d was requested to heat to %.2f", device, temperature));
return false;
}
return send_gcode(gcode);
}
Thumbnail KlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
{
Thumbnail thumbnail = {0};
HTTPClient client;
configure_http_client(client, "/server/files/thumbnails?filename=" + urlEncode(gcode_filename), true, 1000);
char* img_filename_path = NULL;
unsigned char* data_png = NULL;
int http_code = 0;
try
{
http_code = client.GET();
}
catch (...)
{
LOG_LN("Exception while fetching gcode img location");
return thumbnail;
}
if (http_code == 200)
{
JsonDocument doc;
deserializeJson(doc, client.getStream());
img_filename_path = parse_thumbnails(doc);
}
else
{
LOG_F(("Failed to fetch gcode image data: %d\n", http_code))
}
if (img_filename_path == NULL)
{
LOG_LN("No compatible thumbnail found");
return thumbnail;
}
else
{
LOG_F(("Found 32x32 PNG gcode img at %s\n", gcode_filename));
}
client.end();
configure_http_client(client, "/server/files/gcodes/" + urlEncode(img_filename_path), false, 2000);
http_code = 0;
try
{
http_code = client.GET();
}
catch (...)
{
LOG_LN("Exception while fetching gcode img");
return thumbnail;
}
if (http_code == 200)
{
size_t len = client.getSize();
if (len <= 0)
{
LOG_LN("No gcode img data");
return thumbnail;
}
data_png = (unsigned char*)malloc(len + 1);
if (data_png != NULL)
{
if (len != client.getStream().readBytes(data_png, len))
{
LOG_LN("Failed to read gcode img data");
free(data_png);
}
else
{
thumbnail.png = data_png;
thumbnail.size = len;
thumbnail.success = true;
}
}
}
free(img_filename_path);
return thumbnail;
}
KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config)
{
HTTPClient client;
client.setTimeout(1000);
client.setConnectTimeout(1000);
client.begin("http://" + String(config->printer_host) + ":" + String(config->klipper_port) + "/printer/info");
if (config->auth_configured) {
client.addHeader("X-Api-Key", config->printer_auth);
}
int http_code;
try {
http_code = client.GET();
if (http_code == 403)
{
return KlipperConnectionStatus::ConnectAuthRequired;
}
return http_code == 200 ? KlipperConnectionStatus::ConnectOk : KlipperConnectionStatus::ConnectFail;
}
catch (...) {
LOG_LN("Failed to connect");
return KlipperConnectionStatus::ConnectFail;
}
}

View File

@@ -0,0 +1,87 @@
#pragma once
#include "../printer_integration.hpp"
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <list>
typedef struct {
char* name;
float modified;
} KlipperFileSystemFile;
class KlipperPrinter : public BasePrinter
{
private:
unsigned int slicer_estimated_print_time_s{};
unsigned int last_slicer_time_query{};
void configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout);
protected:
unsigned char lock_absolute_relative_mode_swap{};
unsigned char klipper_request_consecutive_fail_count{};
virtual bool send_emergency_stop();
virtual int get_slicer_time_estimate_s();
void init_ui_panels();
int parse_slicer_time_estimate(JsonDocument& in);
void parse_state(JsonDocument& in);
void parse_state_min(JsonDocument &in, PrinterDataMinimal* data);
Macros parse_macros(JsonDocument &in);
int parse_macros_count(JsonDocument &in);
PowerDevices parse_power_devices(JsonDocument &in);
int parse_power_devices_count(JsonDocument &in);
void parse_file_list(JsonDocument &in, std::list<KlipperFileSystemFile> &files, int fetch_limit);
char *parse_thumbnails(JsonDocument &in);
public:
float gcode_offset[3]{};
KlipperPrinter(int index) : BasePrinter(index)
{
supported_features = PrinterFeatureRestart
| PrinterFeatureFirmwareRestart
| PrinterFeatureHome
| PrinterFeatureDisableSteppers
| PrinterFeaturePause
| PrinterFeatureResume
| PrinterFeatureStop
| PrinterFeatureEmergencyStop
| PrinterFeatureExtrude
| PrinterFeatureRetract
| PrinterFeatureCooldown;
supported_temperature_devices = PrinterTemperatureDeviceBed
| PrinterTemperatureDeviceNozzle1;
init_ui_panels();
printer_data.error_screen_features = PrinterFeatureRestart | PrinterFeatureFirmwareRestart;
}
bool move_printer(const char* axis, float amount, bool relative);
bool execute_feature(PrinterFeatures feature);
virtual bool connect();
virtual bool fetch();
virtual PrinterDataMinimal fetch_min();
void disconnect();
virtual Macros get_macros();
virtual int get_macros_count();
bool execute_macro(const char* macro);
virtual PowerDevices get_power_devices();
virtual int get_power_devices_count();
virtual bool set_power_device_state(const char* device_name, bool state);
virtual Files get_files();
virtual bool start_file(const char* filename);
virtual Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);
virtual bool send_gcode(const char* gcode, bool wait = true);
};
enum KlipperConnectionStatus {
ConnectFail = 0,
ConnectOk = 1,
ConnectAuthRequired = 2,
};
KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config);

View File

@@ -0,0 +1,188 @@
#include "klipper_printer_integration.hpp"
#include "lvgl.h"
#include "../../ui/ui_utils.h"
#include "../common/constants.h"
#include <stdio.h>
#include "../semaphore.h"
bool send_gcode_blocking(const char *gcode, bool wait = true)
{
freeze_request_thread();
KlipperPrinter* printer = (KlipperPrinter*)get_current_printer(); // TODO: pass by ref
bool result = printer->send_gcode(gcode);
unfreeze_request_thread();
return result;
}
bool move_printer_blocking(const char* axis, float amount, bool relative)
{
freeze_request_thread();
KlipperPrinter* printer = (KlipperPrinter*)get_current_printer(); // TODO: pass by ref
bool result = printer->move_printer(axis, amount, relative);
unfreeze_request_thread();
return result;
}
static void set_fan_speed_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
char data[16];
sprintf(data, "Fan: %.0f%%", get_current_printer_data()->fan_speed * 100);
lv_label_set_text(label, data);
}
static void set_fan_speed(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
int actual_speed = fan_percent_to_byte(speed);
char gcode[16];
sprintf(gcode, "M106 S%d", actual_speed);
send_gcode_blocking(gcode);
}
FAN_SPEED_COLUMN(set_fan_speed, klipper_fan_speed_columns)
static void set_zoffset_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
KlipperPrinter* printer = (KlipperPrinter*)get_current_printer(); // TODO: pass by ref
char data[24];
sprintf(data, "Z Offset: %.03f", printer->gcode_offset[2]);
lv_label_set_text(label, data);
}
static void set_zoffset_text_ex(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
KlipperPrinter* printer = (KlipperPrinter*)get_current_printer(); // TODO: pass by ref
char data[32];
sprintf(data, "Z Offset: %.03f, Z: %.03f", printer->gcode_offset[2], get_current_printer_data()->position[2]);
lv_label_set_text(label, data);
}
static void set_zoffset(lv_event_t * e){
char* offset = (char*)lv_event_get_user_data(e);
char gcode[48];
sprintf(gcode, "SET_GCODE_OFFSET Z_ADJUST=%s MOVE=1", offset);
send_gcode_blocking(gcode);
}
static void set_z(lv_event_t * e){
void* ptr = lv_event_get_user_data(e);
float value = *(float *)(&ptr);
if (value < 0) {
send_gcode_blocking("SET_GCODE_OFFSET Z=0 MOVE=1");
return;
}
move_printer_blocking("Z", value, false);
}
const char* zoffsets[] = { "-0.01", "-0.025", "-0.05", "-0.2" };
const char* zoffsets_2[] = { "+0.01", "+0.025", "+0.05", "+0.2" };
const char* zabs[] = { "Z=0", "Z=0.1", "Z=1", "Clear" };
const float zabsvalues[] = { 0, 0.1f, 1.0f, -1.0f };
lv_button_column_t zoffset_columns[] = {
{ set_zoffset, zoffsets, (const void**)zoffsets, 4},
{ set_zoffset, zoffsets_2, (const void**)zoffsets_2, 4},
{ set_z, zabs, (const void**)zabsvalues, 4}
};
static void set_speed_mult_text(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char data[16];
sprintf(data, "Speed: %.0f%%", get_current_printer_data()->speed_mult * 100);
lv_label_set_text(label, data);
}
static void set_speed_mult(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
char gcode[16];
sprintf(gcode, "M220 S%d", speed);
send_gcode_blocking(gcode);
}
static void set_speed_mult_offset(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
float result = get_current_printer_data()->speed_mult * 100 + speed;
get_current_printer_data()->speed_mult = result / 100;
char gcode[16];
sprintf(gcode, "M220 S%.0f", result);
send_gcode_blocking(gcode);
}
const char* speed_presets[] = { "50%", "100%", "150%", "200%" };
const int speed_presets_values[] = { 50, 100, 150, 200 };
const char* speed_presets_minus[] = { "-1%", "-5%", "-10%", "-25%" };
const int speed_presets_minus_values[] = { -1, -5, -10, -25 };
const char* speed_presets_plus[] = { "+1%", "+5%", "+10%", "+25%" };
const int speed_presets_plus_values[] = { 1, 5, 10, 25 };
lv_button_column_t speed_mult_columns[] = {
{ set_speed_mult, speed_presets, (const void**)speed_presets_values, 4},
{ set_speed_mult_offset, speed_presets_minus, (const void**)speed_presets_minus_values, 4},
{ set_speed_mult_offset, speed_presets_plus, (const void**)speed_presets_plus_values, 4}
};
static void set_extrude_mult_text(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char data[16];
sprintf(data, "Flow: %.0f%%", get_current_printer_data()->extrude_mult * 100);
lv_label_set_text(label, data);
}
static void set_extrude_mult(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
char gcode[16];
sprintf(gcode, "M221 S%d", speed);
send_gcode_blocking(gcode);
}
static void set_extrude_mult_offset(lv_event_t * e){
int speed = (int)lv_event_get_user_data(e);
float result = get_current_printer_data()->extrude_mult * 100 + speed;
get_current_printer_data()->extrude_mult = result / 100;
char gcode[16];
sprintf(gcode, "M221 S%.0f", result);
send_gcode_blocking(gcode);
}
const char* extrude_presets[] = { "95%", "100%", "105%", "110%" };
const int extrude_presets_values[] = { 95, 100, 105, 110 };
const char* extrude_offset[] = { "+5%", "+1%", "-1%", "-5%" };
const int extrude_offset_values[] = { 5, 1, -1, -5 };
lv_button_column_t extrude_mult_columns[] = {
{ set_extrude_mult, extrude_presets, (const void**)extrude_presets_values, 4},
{ set_extrude_mult_offset, extrude_offset, (const void**)extrude_offset_values, 4}
};
static void open_fan_speed_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_fan_speed_text, klipper_fan_speed_columns, 3);
}
static void open_zoffset_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_zoffset_text_ex, zoffset_columns, get_current_printer_data()->state == PrinterStateIdle ? 3 : 2);
}
static void open_speed_mult_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_speed_mult_text, speed_mult_columns, 3);
}
static void open_extrude_mult_panel(lv_event_t * e){
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_extrude_mult_text, extrude_mult_columns, 2);
}
static PrinterUiPanel klipper_ui_panels[4] {
{ .set_label = (void*)set_fan_speed_text, .open_panel = (void*)open_fan_speed_panel },
{ .set_label = (void*)set_zoffset_text, .open_panel = (void*)open_zoffset_panel },
{ .set_label = (void*)set_speed_mult_text, .open_panel = (void*)open_speed_mult_panel },
{ .set_label = (void*)set_extrude_mult_text, .open_panel = (void*)open_extrude_mult_panel }
};
void KlipperPrinter::init_ui_panels()
{
custom_menus_count = 4;
custom_menus = klipper_ui_panels;
}

View File

@@ -0,0 +1,384 @@
#include "../printer_integration.hpp"
#include "klipper_printer_integration.hpp"
#include <ArduinoJson.h>
int KlipperPrinter::parse_slicer_time_estimate(JsonDocument &in)
{
int time_estimate_s = in["result"]["estimated_time"];
LOG_F(("Got slicer time estimate: %ds\n", time_estimate_s))
return time_estimate_s;
}
void KlipperPrinter::parse_state(JsonDocument &in)
{
JsonObject status = in["result"]["status"];
if (status.containsKey("webhooks"))
{
const char *state = status["webhooks"]["state"];
const char *message = status["webhooks"]["state_message"];
if (strcmp(state, "ready") == 0 && printer_data.state == PrinterStateError)
{
printer_data.state = PrinterStateIdle;
}
else if ((strcmp(state, "shutdown") == 0 || strcmp(state, "error") == 0) && printer_data.state != PrinterStateError)
{
printer_data.state = PrinterStateError;
}
if (message != NULL && (printer_data.state_message == NULL || strcmp(printer_data.state_message, message)))
{
printer_data.state_message = (char *)malloc(strlen(message) + 1);
strcpy(printer_data.state_message, message);
}
}
if (printer_data.state != PrinterStateError)
{
if (status.containsKey("extruder"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] = status["extruder"]["temperature"];
printer_data.target_temperatures[PrinterTemperatureDeviceIndexNozzle1] = status["extruder"]["target"];
bool can_extrude = status["extruder"]["can_extrude"];
printer_data.pressure_advance = status["extruder"]["pressure_advance"];
printer_data.smooth_time = status["extruder"]["smooth_time"];
printer_data.can_extrude = can_extrude == true;
}
if (status.containsKey("heater_bed"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexBed] = status["heater_bed"]["temperature"];
printer_data.target_temperatures[PrinterTemperatureDeviceIndexBed] = status["heater_bed"]["target"];
}
if (status.containsKey("toolhead"))
{
const char *homed_axis = status["toolhead"]["homed_axes"];
printer_data.homed_axis = strcmp(homed_axis, "xyz") == 0;
}
if (status.containsKey("gcode_move"))
{
printer_data.position[0] = status["gcode_move"]["gcode_position"][0];
printer_data.position[1] = status["gcode_move"]["gcode_position"][1];
printer_data.position[2] = status["gcode_move"]["gcode_position"][2];
gcode_offset[0] = status["gcode_move"]["homing_origin"][0];
gcode_offset[1] = status["gcode_move"]["homing_origin"][1];
gcode_offset[2] = status["gcode_move"]["homing_origin"][2];
bool absolute_coords = status["gcode_move"]["absolute_coordinates"];
if (lock_absolute_relative_mode_swap > 0)
{
lock_absolute_relative_mode_swap--;
}
else
{
printer_data.absolute_coords = absolute_coords == true;
}
printer_data.speed_mult = status["gcode_move"]["speed_factor"];
printer_data.extrude_mult = status["gcode_move"]["extrude_factor"];
printer_data.feedrate_mm_per_s = status["gcode_move"]["speed"];
printer_data.feedrate_mm_per_s /= 60; // convert mm/m to mm/s
}
if (status.containsKey("fan"))
{
printer_data.fan_speed = status["fan"]["speed"];
}
if (status.containsKey("virtual_sdcard"))
{
printer_data.print_progress = status["virtual_sdcard"]["progress"];
}
if (status.containsKey("print_stats"))
{
const char *filename = status["print_stats"]["filename"];
if (filename != NULL && (printer_data.print_filename == NULL || strcmp(printer_data.print_filename, filename)))
{
printer_data.print_filename = (char *)malloc(strlen(filename) + 1);
strcpy(printer_data.print_filename, filename);
}
printer_data.elapsed_time_s = status["print_stats"]["total_duration"];
printer_data.printed_time_s = status["print_stats"]["print_duration"];
printer_data.filament_used_mm = status["print_stats"]["filament_used"];
printer_data.total_layers = status["print_stats"]["info"]["total_layer"];
printer_data.current_layer = status["print_stats"]["info"]["current_layer"];
const char *state = status["print_stats"]["state"];
if (state == nullptr)
{
// Continue
}
else if (strcmp(state, "printing") == 0)
{
printer_data.state = PrinterStatePrinting;
}
else if (strcmp(state, "paused") == 0)
{
printer_data.state = PrinterStatePaused;
}
else if (strcmp(state, "complete") == 0 || strcmp(state, "cancelled") == 0 || strcmp(state, "standby") == 0)
{
printer_data.state = PrinterStateIdle;
}
}
if (status.containsKey("display_status"))
{
printer_data.print_progress = status["display_status"]["progress"];
const char *message = status["display_status"]["message"];
if (!global_config.disable_m117_messaging && message != NULL && (printer_data.popup_message == NULL || strcmp(printer_data.popup_message, message)))
{
printer_data.popup_message = (char *)malloc(strlen(message) + 1);
strcpy(printer_data.popup_message, message);
}
}
if (printer_data.state == PrinterStatePrinting && printer_data.print_progress > 0)
{
float remaining_time_s_percentage = (printer_data.printed_time_s / printer_data.print_progress) - printer_data.printed_time_s;
float remaining_time_s_slicer = 0;
if (slicer_estimated_print_time_s > 0)
{
remaining_time_s_slicer = slicer_estimated_print_time_s - printer_data.printed_time_s;
}
if (remaining_time_s_slicer <= 0 || printer_config->remaining_time_calc_mode == REMAINING_TIME_CALC_PERCENTAGE)
{
printer_data.remaining_time_s = remaining_time_s_percentage;
}
else if (printer_config->remaining_time_calc_mode == REMAINING_TIME_CALC_INTERPOLATED)
{
printer_data.remaining_time_s = remaining_time_s_percentage * printer_data.print_progress + remaining_time_s_slicer * (1 - printer_data.print_progress);
}
else if (printer_config->remaining_time_calc_mode == REMAINING_TIME_CALC_SLICER)
{
printer_data.remaining_time_s = remaining_time_s_slicer;
}
}
if (printer_data.remaining_time_s < 0)
{
printer_data.remaining_time_s = 0;
}
if (printer_data.state == PrinterStateIdle)
{
slicer_estimated_print_time_s = 0;
}
}
if (printer_data.state == PrinterStatePrinting && millis() - last_slicer_time_query > 30000 && slicer_estimated_print_time_s <= 0)
{
last_slicer_time_query = millis();
slicer_estimated_print_time_s = get_slicer_time_estimate_s();
}
}
void KlipperPrinter::parse_state_min(JsonDocument &in, PrinterDataMinimal* data)
{
JsonObject status = in["result"]["status"];
if (status.containsKey("webhooks"))
{
const char *state = status["webhooks"]["state"];
if (strcmp(state, "shutdown") == 0)
{
data->state = PrinterState::PrinterStateError;
}
}
if (data->state != PrinterStateError)
{
if (status.containsKey("virtual_sdcard"))
{
data->print_progress = status["virtual_sdcard"]["progress"];
}
if (status.containsKey("print_stats"))
{
const char *state = status["print_stats"]["state"];
if (state == nullptr)
{
data->state = PrinterState::PrinterStateError;
}
else if (strcmp(state, "printing") == 0)
{
data->state = PrinterState::PrinterStatePrinting;
}
else if (strcmp(state, "paused") == 0)
{
data->state = PrinterState::PrinterStatePaused;
}
else if (strcmp(state, "complete") == 0 || strcmp(state, "cancelled") == 0 || strcmp(state, "standby") == 0)
{
data->state = PrinterState::PrinterStateIdle;
}
}
}
}
Macros KlipperPrinter::parse_macros(JsonDocument &in)
{
JsonObject result = in["result"];
Macros macros = {0};
macros.macros = (char **)malloc(sizeof(char *) * 32);
macros.count = 0;
macros.success = true;
for (JsonPair i : result)
{
const char *key = i.key().c_str();
const char *value = i.value().as<String>().c_str();
if (strcmp(value, "CYD_SCREEN_MACRO") == 0)
{
char *macro = (char *)malloc(strlen(key) + 1);
strcpy(macro, key);
macros.macros[macros.count++] = macro;
}
}
return macros;
}
int KlipperPrinter::parse_macros_count(JsonDocument &in)
{
JsonObject result = in["result"];
int count = 0;
for (JsonPair i : result)
{
const char *value = i.value().as<String>().c_str();
if (strcmp(value, "CYD_SCREEN_MACRO") == 0)
{
count++;
}
}
return count;
}
PowerDevices KlipperPrinter::parse_power_devices(JsonDocument &in)
{
PowerDevices power_devices = {0};
JsonArray result = in["result"]["devices"];
power_devices.power_devices = (char **)malloc(sizeof(char *) * 16);
power_devices.power_states = (bool *)malloc(sizeof(bool) * 16);
power_devices.count = 0;
power_devices.success = true;
for (JsonObject i : result)
{
const char *device_name = i["device"];
const char *device_state = i["status"];
power_devices.power_devices[power_devices.count] = (char *)malloc(strlen(device_name) + 1);
strcpy(power_devices.power_devices[power_devices.count], device_name);
power_devices.power_states[power_devices.count] = strcmp(device_state, "on") == 0;
power_devices.count++;
}
return power_devices;
}
int KlipperPrinter::parse_power_devices_count(JsonDocument &in)
{
JsonArray result = in["result"]["devices"];
int count = 0;
for (JsonObject i : result)
{
count++;
}
return count;
}
void KlipperPrinter::parse_file_list(JsonDocument &in, std::list<KlipperFileSystemFile> &files, int fetch_limit)
{
JsonArray result = in["result"];
for (JsonObject file : result)
{
KlipperFileSystemFile 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++;
}
if (file_iter == files.end() && files.size() >= fetch_limit)
continue;
f.name = (char *)malloc(strlen(path) + 1);
if (f.name == NULL)
{
LOG_LN("Failed to allocate memory");
continue;
}
strcpy(f.name, path);
f.modified = modified;
if (file_iter != files.end())
files.insert(file_iter, f);
else
files.push_back(f);
if (files.size() > fetch_limit)
{
auto last_entry = files.back();
if (last_entry.name != NULL)
free(last_entry.name);
files.pop_back();
}
}
}
char *KlipperPrinter::parse_thumbnails(JsonDocument &in)
{
JsonArray result = in["result"];
const char *chosen_thumb = NULL;
for (JsonObject file : result)
{
int width = file["width"];
int height = file["height"];
int size = file["size"];
const char *thumbnail = file["thumbnail_path"];
if (width != height || width != 32)
continue;
if (strcmp(thumbnail + strlen(thumbnail) - 4, ".png"))
continue;
chosen_thumb = thumbnail;
break;
}
if (chosen_thumb != NULL)
{
char* img_filename_path = (char *)malloc(strlen(chosen_thumb) + 1);
strcpy(img_filename_path, chosen_thumb);
return img_filename_path;
}
return NULL;
}

View File

@@ -0,0 +1,304 @@
#include "lv_setup.h"
#include "screen_driver.h"
#include "../conf/global_config.h"
#include "lvgl.h"
#include "../ui/ui_utils.h"
#include <Esp.h>
#include "../ui/serial/serial_console.h"
#ifndef CPU_FREQ_HIGH
#define CPU_FREQ_HIGH 240
#endif
#ifndef CPU_FREQ_LOW
#define CPU_FREQ_LOW 80
#endif
unsigned long last_milis = 0;
void lv_handler()
{
#ifndef CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
if (digitalRead(0) == HIGH)
{
last_milis = millis();
}
else if (millis() - last_milis > 8000)
{
global_config.screen_calibrated = false;
write_global_config();
ESP.restart();
}
#endif
lv_timer_handler();
lv_task_handler();
}
typedef void (*lv_indev_drv_read_cb_t)(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
bool is_screen_in_sleep = false;
lv_timer_t *screen_sleep_timer;
lv_coord_t point[2] = {0};
static lv_indev_drv_read_cb_t original_touch_driver = NULL;
void lv_touch_intercept_calibration(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
original_touch_driver(indev_driver, data);
if (data->state == LV_INDEV_STATE_PR){
lv_coord_t local_point[] = {data->point.x, data->point.y};
while (data->state == LV_INDEV_STATE_PR){
original_touch_driver(indev_driver, data);
delay(20);
}
point[0] = local_point[0];
point[1] = local_point[1];
}
data->state = LV_INDEV_STATE_REL;
}
void lv_touch_intercept(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
original_touch_driver(indev_driver, data);
if (data->state == LV_INDEV_STATE_PR) {
if (is_screen_asleep()) {
while (data->state == LV_INDEV_STATE_PR) {
original_touch_driver(indev_driver, data);
delay(20);
}
data->state = LV_INDEV_STATE_REL;
delay(300); // Some screens don't debounce their signal properly
}
screen_timer_wake();
#ifndef CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
data->point.x = round((data->point.x * global_config.screen_cal_x_mult) + global_config.screen_cal_x_offset);
data->point.y = round((data->point.y * global_config.screen_cal_y_mult) + global_config.screen_cal_y_offset);
#endif // CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
}
}
void lv_do_calibration(){
if (global_config.screen_calibrated){
return;
}
lv_indev_t * display_driver = lv_indev_get_next(NULL);
display_driver->driver->read_cb = lv_touch_intercept_calibration;
lv_obj_clean(lv_scr_act());
lv_obj_clear_flag(lv_scr_act(), LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Calibrate Screen");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * line_x = lv_line_create(lv_scr_act());
lv_obj_t * line_y = lv_line_create(lv_scr_act());
static lv_point_t line_points_x[] = { {0, 10}, {21, 10} };
static lv_point_t line_points_y[] = { {10, 0}, {10, 21} };
lv_line_set_points(line_x, line_points_x, 2);
lv_obj_set_style_line_width(line_x, 1, 0);
lv_line_set_points(line_y, line_points_y, 2);
lv_obj_set_style_line_width(line_y, 1, 0);
#ifdef CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY
lv_obj_align(line_x, LV_ALIGN_TOP_RIGHT, 1, 0);
lv_obj_align(line_y, LV_ALIGN_TOP_RIGHT, -10, 0);
#else
lv_obj_align(line_x, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_align(line_y, LV_ALIGN_TOP_LEFT, 0, 0);
#endif
while (true){
lv_handler();
serial_console::run();
if (point[0] != 0 && point[1] != 0){
break;
}
}
delay(300);
lv_coord_t x1 = point[0];
lv_coord_t y1 = point[1];
point[0] = 0;
point[1] = 0;
lv_obj_del(line_x);
lv_obj_del(line_y);
line_x = lv_line_create(lv_scr_act());
line_y = lv_line_create(lv_scr_act());
lv_line_set_points(line_x, line_points_x, 2);
lv_line_set_points(line_y, line_points_y, 2);
lv_obj_set_style_line_width(line_x, 1, 0);
lv_obj_set_style_line_width(line_y, 1, 0);
#ifdef CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY
lv_obj_align(line_x, LV_ALIGN_BOTTOM_LEFT, 0, -10);
lv_obj_align(line_y, LV_ALIGN_BOTTOM_LEFT, 0, 1);
#else
lv_obj_align(line_x, LV_ALIGN_BOTTOM_RIGHT, 1, -10);
lv_obj_align(line_y, LV_ALIGN_BOTTOM_RIGHT, -10, 1);
#endif
while (true){
lv_handler();
if (point[0] != 0 && point[1] != 0){
break;
}
}
lv_coord_t x2 = point[0];
lv_coord_t y2 = point[1];
#ifdef CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY
int16_t xDist = CYD_SCREEN_HEIGHT_PX - 20;
int16_t yDist = CYD_SCREEN_WIDTH_PX - 20;
#else
int16_t xDist = CYD_SCREEN_WIDTH_PX - 20;
int16_t yDist = CYD_SCREEN_HEIGHT_PX - 20;
#endif
global_config.screen_cal_x_mult = (float)xDist / (float)(x2 - x1);
global_config.screen_cal_x_offset = 10.0 - ((float)x1 * global_config.screen_cal_x_mult);
global_config.screen_cal_y_mult = (float)yDist / (float)(y2 - y1);
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()){
LOG_LN("Calibration failed, please try again");
ESP.restart();
}
global_config.screen_calibrated = true;
write_global_config();
lv_obj_clean(lv_scr_act());
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()
{
if (global_config.brightness < 32)
screen_setBrightness(255);
else
screen_setBrightness(global_config.brightness);
}
void screen_timer_wake()
{
#ifndef CYD_SCREEN_DISABLE_TIMEOUT
lv_timer_reset(screen_sleep_timer);
if (!is_screen_in_sleep){
return;
}
is_screen_in_sleep = false;
set_screen_brightness();
// Reset cpu freq
setCpuFrequencyMhz(CPU_FREQ_HIGH);
LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()))
#endif
}
void screen_timer_sleep(lv_timer_t *timer)
{
#ifndef CYD_SCREEN_DISABLE_TIMEOUT
screen_setBrightness(0);
is_screen_in_sleep = true;
// Screen is off, no need to make the cpu run fast, the user won't notice ;)
setCpuFrequencyMhz(CPU_FREQ_LOW);
LOG_F(("CPU Speed: %d MHz\n", ESP.getCpuFreqMHz()))
#endif
}
void screen_timer_setup()
{
screen_sleep_timer = lv_timer_create(screen_timer_sleep, global_config.screen_timeout * 1000 * 60, NULL);
lv_timer_pause(screen_sleep_timer);
}
void screen_timer_start()
{
lv_timer_resume(screen_sleep_timer);
}
void screen_timer_stop()
{
lv_timer_pause(screen_sleep_timer);
}
void screen_timer_period(unsigned int period)
{
lv_timer_set_period(screen_sleep_timer, period);
}
void set_screen_timer_period()
{
screen_timer_period(global_config.screen_timeout * 1000 * 60);
}
void set_color_scheme()
{
PrinterConfiguration *config = &global_config.printer_config[global_config.printer_index];
lv_disp_t *dispp = lv_disp_get_default();
lv_color_t main_color = {0};
ColorDefinition color_def = color_defs[config->color_scheme];
if (color_defs[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[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[config->color_scheme].primary_color);
}
lv_theme_t *theme = lv_theme_default_init(dispp, main_color, lv_palette_main(color_def.secondary_color), !config->light_mode, &CYD_SCREEN_FONT);
lv_disp_set_theme(dispp, theme);
}
void lv_setup()
{
set_screen_brightness();
lv_indev_t * display_driver = lv_indev_get_next(NULL);
if (original_touch_driver == NULL)
{
original_touch_driver = display_driver->driver->read_cb;
}
set_color_scheme();
#ifndef CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
lv_do_calibration();
#endif // CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
display_driver->driver->read_cb = lv_touch_intercept;
screen_timer_setup();
screen_timer_start();
lv_png_init();
}
bool is_screen_asleep()
{
return is_screen_in_sleep;
}

View File

@@ -0,0 +1,11 @@
#pragma once
void set_screen_brightness();
void set_screen_timer_period();
void screen_timer_wake();
void screen_timer_start();
void screen_timer_stop();
void set_color_scheme();
void lv_setup();
bool is_screen_asleep();
void lv_handler();

View File

@@ -0,0 +1,450 @@
#include "octoprint_printer_integration.hpp"
#include "../../conf/global_config.h"
#include <HTTPClient.h>
#include <UrlEncode.h>
#include <ArduinoJson.h>
#include <list>
const char* COMMAND_CONNECT = "{\"command\":\"connect\"}";
const char* COMMAND_DISCONNECT = "{\"command\":\"disconnect\"}";
const char* COMMAND_HOME = "{\"command\":\"home\",\"axes\":[\"x\",\"y\",\"z\"]}";
const char* COMMAND_PRINT = "{\"command\":\"select\",\"print\":true}";
const char* COMMAND_CANCEL_PRINT = "{\"command\":\"cancel\"}";
const char* COMMAND_PAUSE_PRINT = "{\"command\":\"pause\",\"action\":\"pause\"}";
const char* COMMAND_RESUME_PRINT = "{\"command\":\"pause\",\"action\":\"resume\"}";
const char* COMMAND_EXTRUDE = "{\"command\":\"extrude\",\"amount\":25}";
const char* COMMAND_RETRACT = "{\"command\":\"extrude\",\"amount\":-25}";
void configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout, PrinterConfiguration* printer_config)
{
client.useHTTP10(stream);
if (timeout > 0){
client.setTimeout(timeout);
client.setConnectTimeout(timeout);
}
client.begin("http://" + String(printer_config->printer_host) + ":" + String(printer_config->klipper_port) + url_part);
if (printer_config->auth_configured) {
client.addHeader("X-Api-Key", printer_config->printer_auth);
}
}
bool OctoPrinter::get_request(const char* endpoint, int timeout_ms)
{
HTTPClient client;
if (timeout_ms <= 0)
{
timeout_ms = 500;
}
configure_http_client(client, endpoint, false, timeout_ms, printer_config);
int result = client.GET();
return result >= 200 && result < 300;
}
bool OctoPrinter::post_request(const char* endpoint, const char* body, int timeout_ms)
{
HTTPClient client;
if (timeout_ms <= 0)
{
timeout_ms = 500;
}
LOG_F(("POST >>> %s %s\n", endpoint, body));
configure_http_client(client, endpoint, false, timeout_ms, printer_config);
if (body[0] == '{' || body[0] == '[')
{
client.addHeader("Content-Type", "application/json");
}
int http_code = client.POST(body);
bool result = http_code >= 200 && http_code < 300;
LOG_F(("<<< %d\n", http_code));
return result;
}
bool OctoPrinter::send_gcode(const char* gcode, bool wait)
{
char* gcode_copy = (char*)malloc(sizeof(char) * (strlen(gcode) + 1));
size_t out_buff_size = sizeof(char) * (strlen(gcode) * 2 + 51);
char* out_buff = (char*)malloc(out_buff_size);
strcpy(gcode_copy, gcode);
JsonDocument doc;
JsonArray array = doc["commands"].to<JsonArray>();
const char* last_line_start = gcode_copy;
for (char* iter = gcode_copy;; iter++)
{
char cur_iter = *iter;
if (cur_iter == '\n' || cur_iter == '\0')
{
if (iter != last_line_start)
{
*iter = '\0';
array.add(last_line_start);
}
last_line_start = iter + 1;
}
if (cur_iter == '\0')
{
break;
}
}
if (serializeJson(doc, out_buff, out_buff_size) == out_buff_size)
{
return false;
}
free(gcode_copy);
bool result = post_request("/api/printer/command", out_buff);
free(out_buff);
return result;
}
bool OctoPrinter::move_printer(const char* axis, float amount, bool relative)
{
JsonDocument doc;
char out_buff[512];
out_buff[0] = tolower(axis[0]);
out_buff[1] = '\0';
doc["command"] = "jog";
doc[out_buff] = amount;
doc["absolute"] = !relative;
if (serializeJson(doc, out_buff, 512) >= 512)
{
return false;
}
return post_request("/api/printer/printhead", out_buff);
}
bool OctoPrinter::execute_feature(PrinterFeatures feature)
{
switch (feature)
{
case PrinterFeatureRetryError:
if (no_printer)
{
bool a = post_request("/api/connection", COMMAND_CONNECT);
LOG_F(("Retry error: %d\n", a));
return a;
}
case PrinterFeatureHome:
return post_request("/api/printer/printhead", COMMAND_HOME);
case PrinterFeatureDisableSteppers:
return send_gcode("M18");
case PrinterFeaturePause:
return post_request("/api/job", COMMAND_PAUSE_PRINT);
case PrinterFeatureResume:
return post_request("/api/job", COMMAND_RESUME_PRINT);
case PrinterFeatureStop:
return post_request("/api/job", COMMAND_CANCEL_PRINT);
case PrinterFeatureCooldown:
return set_target_temperature(PrinterTemperatureDeviceNozzle1, 0)
&& set_target_temperature(PrinterTemperatureDeviceBed, 0);
case PrinterFeatureExtrude:
return post_request("/api/printer/tool", COMMAND_EXTRUDE);
case PrinterFeatureRetract:
return post_request("/api/printer/tool", COMMAND_RETRACT);
default:
LOG_F(("Unsupported printer feature %d", feature));
break;
}
return false;
}
bool OctoPrinter::connect()
{
return connection_test_octoprint(printer_config) == OctoConnectionStatus::OctoConnectOk;
}
bool OctoPrinter::fetch()
{
HTTPClient client;
HTTPClient client2;
configure_http_client(client, "/api/printer", true, 1000, printer_config);
int http_code = client.GET();
if (http_code == 200)
{
no_printer = false;
request_consecutive_fail_count = 0;
JsonDocument doc;
deserializeJson(doc, client.getStream());
parse_printer_status(doc);
doc.clear();
configure_http_client(client2, "/api/job", true, 1000, printer_config);
if (client2.GET() == 200)
{
deserializeJson(doc, client2.getStream());
parse_job_state(doc);
}
else
{
printer_data.state = PrinterStateOffline;
return false;
}
}
else if (http_code == 409)
{
no_printer = true;
JsonDocument doc;
deserializeJson(doc, client.getStream());
parse_error(doc);
}
else
{
request_consecutive_fail_count++;
LOG_LN("Failed to fetch printer data");
if (request_consecutive_fail_count >= 5)
{
printer_data.state = PrinterStateOffline;
return false;
}
}
return true;
}
PrinterDataMinimal OctoPrinter::fetch_min()
{
PrinterDataMinimal min = {};
min.success = true;
min.print_progress = 0;
min.power_devices = 0;
min.state = PrinterState::PrinterStateOffline;
{
HTTPClient client;
configure_http_client(client, "/api/printer", true, 1000, printer_config);
int http_code = client.GET();
if (http_code == 200)
{
JsonDocument doc;
deserializeJson(doc, client.getStream());
min.state = parse_printer_state(doc);
}
else if (http_code == 409)
{
min.state = PrinterState::PrinterStateError;
return min;
}
else
{
return min;
}
}
{
HTTPClient client;
configure_http_client(client, "/api/job", true, 1000, printer_config);
if (client.GET() == 200)
{
JsonDocument doc;
deserializeJson(doc, client.getStream());
min.print_progress = parse_job_state_progress(doc);
}
else
{
min.state = PrinterState::PrinterStateError;
}
}
return min;
}
void OctoPrinter::disconnect()
{
}
const char* MACRO_AUTOLEVEL = "Auto-Level (G28+G29)";
const char* MACRO_DISCONNECT = "Disconnect printer";
Macros OctoPrinter::get_macros()
{
if (printer_data.state == PrinterStatePrinting || printer_data.state == PrinterStateOffline)
{
Macros macros = {0};
macros.success = false;
return macros;
}
Macros macros = {0};
macros.count = 2;
macros.macros = (char **)malloc(sizeof(char *) * macros.count);
macros.macros[0] = (char *)malloc(strlen(MACRO_AUTOLEVEL) + 1);
strcpy(macros.macros[0], MACRO_AUTOLEVEL);
macros.macros[1] = (char *)malloc(strlen(MACRO_DISCONNECT) + 1);
strcpy(macros.macros[1], MACRO_DISCONNECT);
macros.success = true;
return macros;
}
int OctoPrinter::get_macros_count()
{
return (printer_data.state == PrinterStatePrinting || printer_data.state == PrinterStateOffline) ? 0 : 2;
}
bool OctoPrinter::execute_macro(const char* macro)
{
if (strcmp(macro, MACRO_AUTOLEVEL) == 0)
{
return send_gcode("G28\nG29");
}
else if (strcmp(macro, MACRO_DISCONNECT) == 0)
{
if (printer_data.state == PrinterStatePrinting || printer_data.state == PrinterStateOffline)
{
return false;
}
return post_request("/api/connection", COMMAND_DISCONNECT);
}
return false;
}
PowerDevices OctoPrinter::get_power_devices()
{
return {};
}
int OctoPrinter::get_power_devices_count()
{
return 0;
}
bool OctoPrinter::set_power_device_state(const char* device_name, bool state)
{
return false;
}
#define OCTO_FILE_FETCH_LIMIT 20
Files OctoPrinter::get_files()
{
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
Files files_result = {0};
HTTPClient client;
JsonDocument filter;
std::list<OctoFileSystemFile> files;
filter["files"][0]["path"] = true;
filter["files"][0]["date"] = true;
filter["files"][0]["origin"] = true;
auto timer_request = millis();
configure_http_client(client, "/api/files?recursive=true", true, 5000, printer_config);
int http_code = client.GET();
auto timer_parse = millis();
if (http_code == 200)
{
JsonDocument doc;
auto parseResult = deserializeJson(doc, client.getStream(), DeserializationOption::Filter(filter));
LOG_F(("Json parse: %s\n", parseResult.c_str()))
parse_file_list(doc, files, OCTO_FILE_FETCH_LIMIT);
}
else
{
return files_result;
}
files_result.available_files = (char**)malloc(sizeof(char*) * files.size());
if (files_result.available_files == NULL){
LOG_LN("Failed to allocate memory");
for (auto file : files){
free(file.name);
}
return files_result;
}
for (auto file : files){
files_result.available_files[files_result.count++] = file.name;
}
files_result.success = true;
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))
return files_result;
return {};
}
bool OctoPrinter::start_file(const char* filename)
{
char buff[512];
sprintf(buff, "/api/files/local/%s", filename);
return post_request(buff, COMMAND_PRINT);
}
Thumbnail OctoPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
{
return {};
}
bool OctoPrinter::set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature)
{
JsonDocument doc;
char out_buff[512];
doc["command"] = "target";
if (device == PrinterTemperatureDevice::PrinterTemperatureDeviceNozzle1)
{
doc["targets"]["tool0"] = temperature;
}
else
{
doc["target"] = temperature;
}
if (serializeJson(doc, out_buff, 512) >= 512)
{
return false;
}
return post_request(device == PrinterTemperatureDevice::PrinterTemperatureDeviceBed ? "/api/printer/bed" : "/api/printer/tool", out_buff);
}
OctoConnectionStatus connection_test_octoprint(PrinterConfiguration* config)
{
HTTPClient client;
configure_http_client(client, "/api/version", false, 1000, config);
int http_code = client.GET();
if (http_code == 200)
{
return OctoConnectionStatus::OctoConnectOk;
}
else if (http_code == 401 || http_code == 403)
{
return OctoConnectionStatus::OctoConnectKeyFail;
}
else
{
return OctoConnectionStatus::OctoConnectFail;
}
}

View File

@@ -0,0 +1,79 @@
#include "../printer_integration.hpp"
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <list>
typedef struct {
char* name;
float modified;
} OctoFileSystemFile;
class OctoPrinter : public BasePrinter
{
protected:
bool no_printer = false;
unsigned char request_consecutive_fail_count{};
void parse_printer_status(JsonDocument& in);
PrinterState parse_printer_state(JsonDocument& in);
void parse_job_state(JsonDocument& in);
float parse_job_state_progress(JsonDocument& in);
void parse_error(JsonDocument& in);
void parse_file_list(JsonDocument &in, std::list<OctoFileSystemFile> &files, int fetch_limit);
bool get_request(const char* endpoint, int timeout_ms = 1000);
void init_ui_panels();
public:
OctoPrinter(int index) : BasePrinter(index)
{
supported_features = PrinterFeatureHome
| PrinterFeatureDisableSteppers
| PrinterFeaturePause
| PrinterFeatureResume
| PrinterFeatureStop
| PrinterFeatureExtrude
| PrinterFeatureRetract
| PrinterFeatureCooldown
| PrinterFeatureRetryError;
supported_temperature_devices = PrinterTemperatureDeviceBed
| PrinterTemperatureDeviceNozzle1;
printer_data.error_screen_features = PrinterFeatureRetryError;
init_ui_panels();
}
bool post_request(const char* endpoint, const char* body, int timeout_ms = 1000);
bool move_printer(const char* axis, float amount, bool relative);
bool execute_feature(PrinterFeatures feature);
bool connect();
bool fetch();
PrinterDataMinimal fetch_min();
void disconnect();
Macros get_macros();
int get_macros_count();
bool execute_macro(const char* macro);
PowerDevices get_power_devices();
int get_power_devices_count();
bool set_power_device_state(const char* device_name, bool state);
Files get_files();
bool start_file(const char* filename);
Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);
bool send_gcode(const char* gcode, bool wait = true);
};
enum OctoConnectionStatus {
OctoConnectFail = 0,
OctoConnectOk = 1,
OctoConnectKeyFail = 2,
};
OctoConnectionStatus connection_test_octoprint(PrinterConfiguration* config);

View File

@@ -0,0 +1,111 @@
#include "octoprint_printer_integration.hpp"
#include "lvgl.h"
#include "../../ui/ui_utils.h"
#include <stdio.h>
#include "../common/constants.h"
const char* COMMAND_EXTRUDE_MULT = "{\"command\":\"flowrate\",\"factor\":%d}";
#define OCTO_TIMEOUT_POPUP_MESSAGES 4000
static void set_fan_speed_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, "Fan");
}
static void set_speed_mult_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, "Speed");
}
static void set_extruder_mult_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, "Flowrate");
}
bool get_range(lv_event_t * e, int min, int max, int* out)
{
char buff[64];
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
const char * txt = lv_textarea_get_text(ta);
if (txt == NULL || *txt == '\0')
{
return false;
}
int parsed_input = atoi(txt);
if (parsed_input < min || parsed_input > max)
{
sprintf(buff, "Value out of range (%d -> %d)", min, max);
lv_create_popup_message(buff, OCTO_TIMEOUT_POPUP_MESSAGES);
return false;
}
*out = parsed_input;
return true;
}
static void set_fan_speed(lv_event_t * e)
{
int speed = (int)lv_event_get_user_data(e);
int actual_fan_speed = fan_percent_to_byte(speed);
char buff[16];
sprintf(buff, "M106 S%d", actual_fan_speed);
((OctoPrinter*)get_current_printer())->send_gcode(buff);
}
FAN_SPEED_COLUMN(set_fan_speed, octo_fan_speed_columns)
static void open_fan_speed_panel(lv_event_t * e)
{
lv_create_fullscreen_button_matrix_popup(lv_scr_act(), set_fan_speed_text, octo_fan_speed_columns, 3);
}
static void set_speed_mult(lv_event_t * e)
{
int speed_mult = 0;
if (get_range(e, 50, 300, &speed_mult))
{
char buff[16];
sprintf(buff, "M220 S%d", speed_mult);
((OctoPrinter*)get_current_printer())->send_gcode(buff);
}
}
static void open_speed_mult_keypad(lv_event_t * e)
{
lv_create_keyboard_text_entry(set_speed_mult, "New speed multiplier %", LV_KEYBOARD_MODE_NUMBER);
}
static void set_extrude_mult(lv_event_t * e)
{
int extrude_mult = 0;
if (get_range(e, 75, 125, &extrude_mult))
{
char buff[64];
sprintf(buff, COMMAND_EXTRUDE_MULT, extrude_mult);
((OctoPrinter*)get_current_printer())->post_request("/api/printer/tool", buff);
}
}
static void open_extrude_mult_keypad(lv_event_t * e)
{
lv_create_keyboard_text_entry(set_extrude_mult, "New extrude multiplier %", LV_KEYBOARD_MODE_NUMBER);
}
static PrinterUiPanel klipper_ui_panels[4] {
{ .set_label = (void*)set_fan_speed_text, .open_panel = (void*)open_fan_speed_panel },
{ .set_label = (void*)set_speed_mult_text, .open_panel = (void*)open_speed_mult_keypad },
{ .set_label = (void*)set_extruder_mult_text, .open_panel = (void*)open_extrude_mult_keypad },
};
void OctoPrinter::init_ui_panels()
{
custom_menus_count = 3;
custom_menus = klipper_ui_panels;
}

View File

@@ -0,0 +1,170 @@
#include "../printer_integration.hpp"
#include "octoprint_printer_integration.hpp"
#include <ArduinoJson.h>
PrinterState OctoPrinter::parse_printer_state(JsonDocument& in)
{
JsonObject flags = in["state"]["flags"];
bool cancelling = flags["cancelling"];
bool closedOrError = flags["closedOrError"];
bool error = flags["error"];
bool finishing = flags["finishing"];
bool operational = flags["operational"];
bool paused = flags["paused"];
bool pausing = flags["pausing"];
bool printing = flags["printing"];
bool ready = flags["ready"];
bool resuming = flags["resuming"];
bool sdReady = flags["sdReady"];
if (printing || resuming)
{
return PrinterState::PrinterStatePrinting;
}
else if (pausing || paused)
{
return PrinterState::PrinterStatePaused;
}
else if (cancelling || finishing || ready)
{
return PrinterState::PrinterStateIdle;
}
return PrinterState::PrinterStateError;
}
void OctoPrinter::parse_printer_status(JsonDocument& in)
{
const char* text = in["state"]["text"];
printer_data.state = parse_printer_state(in);
if (printer_data.state == PrinterState::PrinterStateError)
{
if (text != NULL && (printer_data.state_message == NULL || strcmp(printer_data.state_message, text)))
{
printer_data.state_message = (char *)malloc(strlen(text) + 1);
strcpy(printer_data.state_message, text);
}
}
JsonObject temperature = in["temperature"];
if (temperature.containsKey("bed"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexBed] = temperature["bed"]["actual"];
printer_data.target_temperatures[PrinterTemperatureDeviceIndexBed] = temperature["bed"]["target"];
}
if (temperature.containsKey("tool0"))
{
printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] = temperature["tool0"]["actual"];
printer_data.target_temperatures[PrinterTemperatureDeviceIndexNozzle1] = temperature["tool0"]["target"];
}
printer_data.can_extrude = printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] >= MIN_EXTRUDER_EXTRUDE_TEMP;
printer_data.homed_axis = true;
}
void OctoPrinter::parse_job_state(JsonDocument& in)
{
JsonObject job = in["job"];
if (job.containsKey("file"))
{
const char* name = job["file"]["name"];
if (name != NULL && (printer_data.print_filename == NULL || strcmp(printer_data.print_filename, name)))
{
printer_data.print_filename = (char *)malloc(strlen(name) + 1);
strcpy(printer_data.print_filename, name);
}
}
if (job.containsKey("filament") && job["filament"].as<JsonObject>() != NULL && job["filament"].containsKey("tool0"))
{
printer_data.filament_used_mm = job["filament"]["tool0"]["length"];
}
JsonObject progress = in["progress"];
float completion = progress["completion"];
printer_data.print_progress = completion / 100;
printer_data.elapsed_time_s = progress["printTime"];
printer_data.printed_time_s = progress["printTime"];
printer_data.remaining_time_s = progress["printTimeLeft"];
}
float OctoPrinter::parse_job_state_progress(JsonDocument& in)
{
float completion = in["progress"]["completion"];
return completion / 100;
}
void OctoPrinter::parse_error(JsonDocument& in)
{
const char* error = in["error"];
if (error != NULL)
{
printer_data.state = PrinterState::PrinterStateError;
if (printer_data.state_message == NULL || strcmp(printer_data.state_message, error))
{
printer_data.state_message = (char *)malloc(strlen(error) + 1);
strcpy(printer_data.state_message, error);
}
}
}
void OctoPrinter::parse_file_list(JsonDocument &in, std::list<OctoFileSystemFile> &files, int fetch_limit)
{
JsonArray result = in["files"];
for (JsonObject file : result)
{
const char *path = file["path"];
if (strcmp("local", file["origin"]))
{
LOG_F(("Skipping non-local file %s\n", path))
continue;
}
OctoFileSystemFile f = {0};
float modified = file["date"];
auto file_iter = files.begin();
while (file_iter != files.end())
{
if ((*file_iter).modified < modified)
break;
file_iter++;
}
if (file_iter == files.end() && files.size() >= fetch_limit)
continue;
f.name = (char *)malloc(strlen(path) + 1);
if (f.name == NULL)
{
LOG_LN("Failed to allocate memory");
continue;
}
strcpy(f.name, path);
f.modified = modified;
if (file_iter != files.end())
files.insert(file_iter, f);
else
files.push_back(f);
if (files.size() > fetch_limit)
{
auto last_entry = files.back();
if (last_entry.name != NULL)
free(last_entry.name);
files.pop_back();
}
}
}

View File

@@ -0,0 +1,141 @@
#include "printer_integration.hpp"
#include "lv_setup.h"
#include "screen_driver.h"
#include <HardwareSerial.h>
static char blank[] = { '\0' };
static unsigned char current_printer_index = 0;
static unsigned char last_announced_printer_index = 0;
static unsigned char total_printers;
static BasePrinter** registered_printers;
static PrinterDataMinimal* minimal_data_copy;
static PrinterData* printer_data_copy;
BasePrinter::BasePrinter(unsigned char index)
{
config_index = index;
printer_config = &global_config.printer_config[index];
memset(&printer_data, 0, sizeof(PrinterData));
// TODO: Fetch printer config and global config
}
PrinterData* BasePrinter::AnnouncePrinterData()
{
char* old_state_message = printer_data_copy->state_message;
char* old_print_filename = printer_data_copy->print_filename;
char* old_popup_message = printer_data_copy->popup_message;
PrinterState old_state = printer_data_copy->state;
bool no_free = current_printer_index != last_announced_printer_index;
last_announced_printer_index = current_printer_index;
memcpy(printer_data_copy, &printer_data, sizeof(PrinterData));
if (printer_data_copy->state_message == NULL)
{
printer_data_copy->state_message = blank;
}
if (printer_data_copy->print_filename == NULL)
{
printer_data_copy->print_filename = blank;
}
if (printer_data_copy->popup_message == NULL)
{
printer_data_copy->popup_message = blank;
}
if (old_state_message != printer_data_copy->state_message && old_state_message != NULL && old_state_message != blank && !no_free)
{
LOG_F(("Freeing state message '%s' (%x)\n", old_state_message, old_state_message));
free(old_state_message);
}
if (old_print_filename != printer_data_copy->print_filename && old_print_filename != NULL && old_print_filename != blank && !no_free)
{
LOG_F(("Freeing print filename '%s' (%x)\n", old_print_filename, old_print_filename));
free(old_print_filename);
}
if (old_state != printer_data_copy->state)
{
lv_msg_send(DATA_PRINTER_STATE, get_current_printer());
}
if (old_popup_message != printer_data_copy->popup_message)
{
if (old_popup_message != NULL && old_popup_message != blank && !no_free)
{
LOG_F(("Freeing popup message '%s' (%x)\n", old_popup_message, old_popup_message));
free(old_popup_message);
}
if (printer_data_copy->popup_message != NULL && printer_data_copy->popup_message != blank)
{
lv_msg_send(DATA_PRINTER_POPUP, get_current_printer());
}
}
lv_msg_send(DATA_PRINTER_DATA, get_current_printer());
return printer_data_copy;
}
void initialize_printers(BasePrinter** printers, unsigned char total)
{
LOG_F(("Initializing %d printers\n", total))
printer_data_copy = (PrinterData*)malloc(sizeof(PrinterData));
minimal_data_copy = (PrinterDataMinimal*)malloc(sizeof(PrinterDataMinimal) * total);
memset(printer_data_copy, 0, sizeof(PrinterData));
memset(minimal_data_copy, 0, sizeof(PrinterDataMinimal) * total);
registered_printers = printers;
total_printers = total;
}
BasePrinter* get_current_printer()
{
return get_printer(current_printer_index);
}
BasePrinter* get_printer(int idx)
{
return registered_printers[idx];
}
bool BasePrinter::supports_feature(PrinterFeatures feature)
{
return supported_features & feature == feature;
}
int get_current_printer_index()
{
return current_printer_index;
}
PrinterData* get_current_printer_data()
{
return printer_data_copy;
}
unsigned int get_printer_count()
{
return total_printers;
}
void announce_printer_data_minimal(PrinterDataMinimal* printer_data)
{
memcpy(minimal_data_copy, printer_data, sizeof(PrinterDataMinimal) * total_printers);
lv_msg_send(DATA_PRINTER_MINIMAL, get_current_printer());
}
PrinterDataMinimal* get_printer_data_minimal(int idx)
{
return &(minimal_data_copy[idx]);
}
void set_current_printer(int idx)
{
current_printer_index = idx;
global_config_set_printer(idx);
set_color_scheme();
set_invert_display();
}

View File

@@ -0,0 +1,201 @@
#pragma once
#include "../conf/global_config.h"
#include <esp_task_wdt.h>
#define MIN_EXTRUDER_EXTRUDE_TEMP 175
enum PrinterFeatures {
PrinterFeatureRestart = BIT(0),
PrinterFeatureFirmwareRestart = BIT(1),
PrinterFeatureHome = BIT(2),
PrinterFeatureDisableSteppers = BIT(3),
PrinterFeaturePause = BIT(4),
PrinterFeatureResume = BIT(5),
PrinterFeatureStop = BIT(6),
PrinterFeatureEmergencyStop = BIT(7),
PrinterFeatureExtrude = BIT(8),
PrinterFeatureRetract = BIT(9),
PrinterFeatureIgnoreError = BIT(10),
PrinterFeatureContinueError = BIT(11),
PrinterFeatureCooldown = BIT(12),
PrinterFeatureRetryError = BIT(13),
};
inline PrinterFeatures operator|(PrinterFeatures a, PrinterFeatures b)
{
return static_cast<PrinterFeatures>(static_cast<int>(a) | static_cast<int>(b));
}
enum PrinterTemperatureDevice
{
PrinterTemperatureDeviceBed = BIT(0),
PrinterTemperatureDeviceNozzle1 = BIT(1),
PrinterTemperatureDeviceNozzle2 = BIT(2),
PrinterTemperatureDeviceNozzle3 = BIT(3),
PrinterTemperatureDeviceNozzle4 = BIT(4),
PrinterTemperatureDeviceNozzle5 = BIT(5),
PrinterTemperatureDeviceNozzle6 = BIT(6),
PrinterTemperatureDeviceNozzle7 = BIT(7),
PrinterTemperatureDeviceNozzle8 = BIT(8),
PrinterTemperatureDeviceChamber = BIT(9),
};
enum PrinterTemperatureDeviceIndex
{
PrinterTemperatureDeviceIndexBed = 0,
PrinterTemperatureDeviceIndexNozzle1 = 1,
PrinterTemperatureDeviceIndexNozzle2 = 2,
PrinterTemperatureDeviceIndexNozzle3 = 3,
PrinterTemperatureDeviceIndexNozzle4 = 4,
PrinterTemperatureDeviceIndexNozzle5 = 5,
PrinterTemperatureDeviceIndexNozzle6 = 6,
PrinterTemperatureDeviceIndexNozzle7 = 7,
PrinterTemperatureDeviceIndexNozzle8 = 8,
PrinterTemperatureDeviceIndexChamber = 9,
};
inline PrinterTemperatureDevice operator|(PrinterTemperatureDevice a, PrinterTemperatureDevice b)
{
return static_cast<PrinterTemperatureDevice>(static_cast<int>(a) | static_cast<int>(b));
}
enum PrinterState {
PrinterStateOffline = 0,
PrinterStateError = 1,
PrinterStateIdle = 2,
PrinterStatePrinting = 3,
PrinterStatePaused = 4,
};
typedef struct _PrinterData {
union {
struct {
bool can_extrude : 1;
bool homed_axis : 1;
bool absolute_coords : 1;
};
unsigned char rawState;
};
PrinterState state;
char* state_message;
char* popup_message;
float temperatures[10];
float target_temperatures[10];
float position[3];
float elapsed_time_s;
float printed_time_s;
float remaining_time_s;
float filament_used_mm;
char* print_filename;
float print_progress; // 0 -> 1
float fan_speed; // 0 -> 1
float speed_mult;
float extrude_mult;
int total_layers;
int current_layer;
float pressure_advance;
float smooth_time;
int feedrate_mm_per_s;
PrinterFeatures error_screen_features;
} PrinterData;
typedef struct {
PrinterState state;
float print_progress; // 0 -> 1
unsigned int power_devices;
bool success;
} PrinterDataMinimal;
typedef struct {
char** macros;
unsigned int count;
bool success;
} Macros;
typedef struct {
char** power_devices;
bool* power_states;
unsigned int count;
bool success;
} PowerDevices;
typedef struct {
char** available_files;
unsigned int count;
bool success;
} Files;
typedef struct {
void* set_label; // type lv_event_cb_t
void* open_panel; // type lv_event_cb_t
} PrinterUiPanel;
typedef struct {
bool success;
unsigned int size;
unsigned char* png;
} Thumbnail;
enum HttpRequestType
{
HttpPost,
HttpGet
};
class BasePrinter
{
protected:
unsigned char config_index{};
PrinterData printer_data{};
public:
short popup_message_timeout_s = 10;
bool no_confirm_print_file = false;
PrinterConfiguration* printer_config{};
PrinterFeatures supported_features{};
PrinterTemperatureDevice supported_temperature_devices{};
PrinterUiPanel* custom_menus{};
unsigned char custom_menus_count{};
virtual bool move_printer(const char* axis, float amount, bool relative) = 0;
virtual bool execute_feature(PrinterFeatures feature) = 0;
virtual bool connect() = 0;
virtual bool fetch() = 0;
virtual PrinterDataMinimal fetch_min() = 0;
virtual void disconnect() = 0;
// Free macros externally when done
virtual Macros get_macros() = 0;
virtual int get_macros_count() = 0;
virtual bool execute_macro(const char* macro) = 0;
// Free power devices externally when done
virtual PowerDevices get_power_devices() = 0;
virtual int get_power_devices_count() = 0;
virtual bool set_power_device_state(const char* device_name, bool state) = 0;
// Free files externally when done
virtual Files get_files() = 0;
virtual bool start_file(const char* filename) = 0;
// Free thumbnail externally when done
virtual Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename) = 0;
virtual bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature) = 0;
BasePrinter(unsigned char index);
PrinterData* AnnouncePrinterData();
bool supports_feature(PrinterFeatures feature);
};
#define DATA_PRINTER_STATE 1
#define DATA_PRINTER_DATA 2
#define DATA_PRINTER_TEMP_PRESET 3
#define DATA_PRINTER_MINIMAL 4
#define DATA_PRINTER_POPUP 5
BasePrinter* get_current_printer();
BasePrinter* get_printer(int idx);
void initialize_printers(BasePrinter** printers, unsigned char total);
PrinterData* get_current_printer_data();
unsigned int get_printer_count();
void announce_printer_data_minimal(PrinterDataMinimal* printer_data);
PrinterDataMinimal* get_printer_data_minimal(int idx);
int get_current_printer_index();
void set_current_printer(int idx);

View File

@@ -1,230 +0,0 @@
#include "screen_driver.h"
#include <SPI.h>
#include <TFT_eSPI.h>
#include "../conf/global_config.h"
#include "lvgl.h"
SPIClass touchscreen_spi = SPIClass(HSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
uint32_t LV_EVENT_GET_COMP_CHILD;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[TFT_WIDTH * TFT_HEIGHT / 10];
TFT_eSPI tft = TFT_eSPI();
bool isScreenInSleep = false;
lv_timer_t *screenSleepTimer;
TS_Point touchscreen_point()
{
TS_Point p = touchscreen.getPoint();
p.x = round((p.x * global_config.screenCalXMult) + global_config.screenCalXOffset);
p.y = round((p.y * global_config.screenCalYMult) + global_config.screenCalYOffset);
return p;
}
void touchscreen_calibrate(bool force)
{
if (global_config.screenCalibrated && !force)
{
return;
}
tft.fillScreen(TFT_BLACK);
tft.setCursor(20, 140);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(2);
tft.println("Calibrate Screen");
TS_Point p;
int16_t x1, y1, x2, y2;
while (touchscreen.touched())
;
tft.drawFastHLine(0, 10, 20, ILI9341_WHITE);
tft.drawFastVLine(10, 0, 20, ILI9341_WHITE);
while (!touchscreen.touched())
;
delay(50);
p = touchscreen.getPoint();
x1 = p.x;
y1 = p.y;
tft.drawFastHLine(0, 10, 20, ILI9341_BLACK);
tft.drawFastVLine(10, 0, 20, ILI9341_BLACK);
delay(500);
while (touchscreen.touched())
;
tft.drawFastHLine(300, 230, 20, ILI9341_WHITE);
tft.drawFastVLine(310, 220, 20, ILI9341_WHITE);
while (!touchscreen.touched())
;
delay(50);
p = touchscreen.getPoint();
x2 = p.x;
y2 = p.y;
tft.drawFastHLine(300, 230, 20, ILI9341_BLACK);
tft.drawFastVLine(310, 220, 20, ILI9341_BLACK);
int16_t xDist = 320 - 40;
int16_t yDist = 240 - 40;
global_config.screenCalXMult = (float)xDist / (float)(x2 - x1);
global_config.screenCalXOffset = 20.0 - ((float)x1 * global_config.screenCalXMult);
global_config.screenCalYMult = (float)yDist / (float)(y2 - y1);
global_config.screenCalYOffset = 20.0 - ((float)y1 * global_config.screenCalYMult);
global_config.screenCalibrated = true;
WriteGlobalConfig();
}
void screen_setBrightness(byte brightness)
{
analogWrite(TFT_BL, brightness);
}
void set_screen_brightness()
{
if (global_config.brightness < 32)
screen_setBrightness(255);
else
screen_setBrightness(global_config.brightness);
}
void screen_timer_wake()
{
lv_timer_reset(screenSleepTimer);
isScreenInSleep = false;
set_screen_brightness();
}
void screen_timer_sleep(lv_timer_t *timer)
{
screen_setBrightness(0);
isScreenInSleep = true;
}
void screen_timer_setup()
{
screenSleepTimer = lv_timer_create(screen_timer_sleep, global_config.screenTimeout * 1000 * 60, NULL);
lv_timer_pause(screenSleepTimer);
}
void screen_timer_start()
{
lv_timer_resume(screenSleepTimer);
}
void screen_timer_stop()
{
lv_timer_pause(screenSleepTimer);
}
void screen_timer_period(uint32_t period)
{
lv_timer_set_period(screenSleepTimer, period);
}
void set_screen_timer_period()
{
screen_timer_period(global_config.screenTimeout * 1000 * 60);
}
void screen_lv_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void screen_lv_touchRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
if (touchscreen.tirqTouched() && touchscreen.touched())
{
lv_timer_reset(screenSleepTimer);
// dont pass first touch after power on
if (isScreenInSleep)
{
screen_timer_wake();
while (touchscreen.touched())
;
return;
}
TS_Point p = touchscreen_point();
data->state = LV_INDEV_STATE_PR;
data->point.x = p.x;
data->point.y = p.y;
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
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_disp_set_theme(dispp, theme);
}
void set_invert_display(){
tft.invertDisplay(global_config.invertColors);
}
void screen_setup()
{
touchscreen_spi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreen_spi);
touchscreen.setRotation(global_config.rotateScreen ? 3 : 1);
lv_init();
tft.init();
tft.setRotation(global_config.rotateScreen ? 3 : 1);
tft.fillScreen(TFT_BLACK);
set_screen_brightness();
set_invert_display();
touchscreen_spi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreen_spi);
touchscreen_calibrate();
lv_disp_draw_buf_init(&draw_buf, buf, NULL, TFT_WIDTH * TFT_HEIGHT / 10);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = TFT_HEIGHT;
disp_drv.ver_res = TFT_WIDTH;
disp_drv.flush_cb = screen_lv_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = screen_lv_touchRead;
lv_indev_drv_register(&indev_drv);
screen_timer_setup();
screen_timer_start();
/*Initialize the graphics library */
LV_EVENT_GET_COMP_CHILD = lv_event_register_id();
set_color_scheme();
}

View File

@@ -1,32 +1,6 @@
#pragma once #pragma once
// Adapted from https://github.com/xperiments-in/xtouch/blob/main/src/devices/2.8/screen.h // Adapted from https://github.com/xperiments-in/xtouch/blob/main/src/devices/2.8/screen.h
#ifndef _SCREEN_DRIVER_INIT void screen_setBrightness(unsigned char brightness);
#define _SCREEN_DRIVER_INIT
#include <XPT2046_Touchscreen.h>
#include <TFT_eSPI.h>
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
TS_Point touchscreen_point();
void touchscreen_calibrate(bool force = false);
void screen_setBrightness(byte brightness);
void screen_timer_setup();
void screen_timer_start();
void screen_timer_stop();
void screen_timer_period(uint32_t period);
void set_color_scheme();
void screen_setup(); void screen_setup();
void set_invert_display(); void set_invert_display();
void screen_timer_wake();
void set_screen_timer_period();
void set_screen_brightness();
extern TFT_eSPI tft;
#endif // _SCREEN_DRIVER_INIT

View File

@@ -0,0 +1,28 @@
#include "semaphore.h"
#include <UrlEncode.h>
#include <esp_task_wdt.h>
SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore;
void semaphore_init(){
freezeRenderThreadSemaphore = xSemaphoreCreateMutex();
freezeRequestThreadSemaphore = xSemaphoreCreateMutex();
xSemaphoreGive(freezeRenderThreadSemaphore);
xSemaphoreGive(freezeRequestThreadSemaphore);
}
void freeze_request_thread(){
xSemaphoreTake(freezeRequestThreadSemaphore, portMAX_DELAY);
}
void unfreeze_request_thread(){
xSemaphoreGive(freezeRequestThreadSemaphore);
}
void freeze_render_thread(){
xSemaphoreTake(freezeRenderThreadSemaphore, portMAX_DELAY);
}
void unfreeze_render_thread(){
xSemaphoreGive(freezeRenderThreadSemaphore);
}

View File

@@ -0,0 +1,11 @@
#pragma once
void semaphore_init();
void freeze_request_thread();
void unfreeze_request_thread();
// Don't use unless you want trouble
void freeze_render_thread();
// Don't use unless you want trouble
void unfreeze_render_thread();

View File

@@ -0,0 +1,230 @@
/*
ESP32-OTA-Pull - a library for doing "pull" based OTA ("Over The Air") firmware
updates, where the image updates are posted on the web.
MIT License
Copyright (c) 2022-3 Mikal Hart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Update.h>
#include <WiFi.h>
class ESP32OTAPull
{
public:
enum ActionType { DONT_DO_UPDATE, UPDATE_BUT_NO_BOOT, UPDATE_AND_BOOT };
// Return codes from CheckForOTAUpdate
enum ErrorCode { UPDATE_AVAILABLE = -3, NO_UPDATE_PROFILE_FOUND = -2, NO_UPDATE_AVAILABLE = -1, UPDATE_OK = 0, HTTP_FAILED = 1, WRITE_ERROR = 2, JSON_PROBLEM = 3, OTA_UPDATE_FAIL = 4 };
private:
void (*Callback)(int offset, int totallength) = NULL;
ActionType Action = UPDATE_AND_BOOT;
String Board = ARDUINO_BOARD;
String Device = "";
String Config = "";
String CVersion = "";
bool DowngradesAllowed = false;
int DownloadJson(const char* URL, String& payload)
{
HTTPClient http;
http.begin(URL);
// Send HTTP GET request
int httpResponseCode = http.GET();
if (httpResponseCode == 200)
{
payload = http.getString();
}
// Free resources
http.end();
return httpResponseCode;
}
int DoOTAUpdate(const char* URL, ActionType Action)
{
HTTPClient http;
http.begin(URL);
// Send HTTP GET request
int httpResponseCode = http.GET();
if (httpResponseCode == 200)
{
int totalLength = http.getSize();
// this is required to start firmware update process
if (!Update.begin(UPDATE_SIZE_UNKNOWN))
return OTA_UPDATE_FAIL;
// create buffer for read
uint8_t buff[1280] = { 0 };
// get tcp stream
WiFiClient* stream = http.getStreamPtr();
// read all data from server
int offset = 0;
while (http.connected() && offset < totalLength)
{
size_t sizeAvail = stream->available();
if (sizeAvail > 0)
{
size_t bytes_to_read = min(sizeAvail, sizeof(buff));
size_t bytes_read = stream->readBytes(buff, bytes_to_read);
size_t bytes_written = Update.write(buff, bytes_read);
if (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;
if (Callback != NULL)
Callback(offset, totalLength);
}
}
if (offset == totalLength)
{
Update.end(true);
delay(1000);
// Restart ESP32 to see changes
if (Action == UPDATE_BUT_NO_BOOT)
return UPDATE_OK;
ESP.restart();
}
return WRITE_ERROR;
}
http.end();
return httpResponseCode;
}
public:
/// @brief Return the version string of the binary, as reported by the JSON
/// @return The firmware version
String GetVersion()
{
return CVersion;
}
/// @brief Override the default "Device" id (MAC Address)
/// @param device A string identifying the particular device (instance) (typically e.g., a MAC address)
/// @return The current ESP32OTAPull object for chaining
ESP32OTAPull &OverrideDevice(const char *device)
{
Device = device;
return *this;
}
/// @brief Override the default "Board" value of ARDUINO_BOARD
/// @param board A string identifying the board (class) being targeted
/// @return The current ESP32OTAPull object for chaining
ESP32OTAPull &OverrideBoard(const char *board)
{
Board = board;
return *this;
}
/// @brief Specify a configuration string that must match any "Config" in JSON
/// @param config An arbitrary string showing the current configuration
/// @return The current ESP32OTAPull object for chaining
ESP32OTAPull &SetConfig(const char *config)
{
Config = config;
return *this;
}
/// @brief Specify whether downgrades (posted version is lower) are allowed
/// @param allow_downgrades true if downgrades are allowed
/// @return The current ESP32OTAPull object for chaining
ESP32OTAPull &AllowDowngrades(bool allow_downgrades)
{
DowngradesAllowed = allow_downgrades;
return *this;
}
/// @brief Specify a callback function to monitor update progress
/// @param callback Pointer to a function that is called repeatedly during update
/// @return The current ESP32OTAPull object for chaining
ESP32OTAPull &SetCallback(void (*callback)(int offset, int totallength))
{
Callback = callback;
return *this;
}
/// @brief The main entry point for OTA Update
/// @param JSON_URL The URL for the JSON filter file
/// @param CurrentVersion The version # of the current (i.e. to be replaced) sketch
/// @param ActionType The action to be performed. May be any of DONT_DO_UPDATE, UPDATE_BUT_NO_BOOT, UPDATE_AND_BOOT (default)
/// @return ErrorCode or HTTP failure code (see enum above)
int CheckForOTAUpdate(const char* JSON_URL, const char *CurrentVersion, ActionType Action = UPDATE_AND_BOOT)
{
CurrentVersion = CurrentVersion == NULL ? "" : CurrentVersion;
// Downloading OTA Json...
String Payload;
int httpResponseCode = DownloadJson(JSON_URL, Payload);
if (httpResponseCode != 200)
return httpResponseCode > 0 ? httpResponseCode : HTTP_FAILED;
// Deserialize the JSON file downloaded from user's site
JsonDocument doc;
DeserializationError deserialization = deserializeJson(doc, Payload.c_str());
if (deserialization != DeserializationError::Ok)
return JSON_PROBLEM;
String DeviceName = Device.isEmpty() ? WiFi.macAddress() : Device;
String BoardName = Board.isEmpty() ? ARDUINO_BOARD : Board;
String ConfigName = Config.isEmpty() ? "" : Config;
bool foundProfile = false;
// Step through the configurations looking for a match
for (auto config : doc["Configurations"].as<JsonArray>())
{
String CBoard = config["Board"].isNull() ? "" : (const char *)config["Board"];
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"];
//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))
{
if (CVersion.isEmpty() || CVersion > String(CurrentVersion) ||
(DowngradesAllowed && CVersion != String(CurrentVersion)))
return Action == DONT_DO_UPDATE ? UPDATE_AVAILABLE : DoOTAUpdate(config["URL"], Action);
foundProfile = true;
}
}
return foundProfile ? NO_UPDATE_AVAILABLE : NO_UPDATE_PROFILE_FOUND;
}
};

View File

@@ -2,61 +2,40 @@
#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"
#include "ui/nav_buttons.h" #include "ui/nav_buttons.h"
#include <Esp.h>
static void event_handler(lv_event_t * e){ #include "core/lv_setup.h"
lv_event_code_t code = lv_event_get_code(e); #include "ui/ota_setup.h"
if(code == LV_EVENT_CLICKED) {
global_config.version = 0;
WriteGlobalConfig();
ESP.restart();
}
}
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println("Hello World"); serial_console::greet();
LoadGlobalConfig(); load_global_config();
screen_setup(); screen_setup();
Serial.println("Screen init done"); lv_setup();
LOG_LN("Screen init done");
wifi_init(); wifi_init();
ip_setup(); ota_init();
ip_init();
data_setup(); data_setup();
nav_style_setup(); nav_style_setup();
main_ui_setup(); main_ui_setup();
/*
lv_obj_clean(lv_scr_act());
lv_obj_t * label;
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, 0);
label = lv_label_create(btn1);
lv_label_set_text(label, "Reset Configuration");
lv_obj_center(label);
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_set_width(slider, 200);
lv_obj_align(slider, LV_ALIGN_CENTER, 0, 40);
lv_slider_set_range(slider, 0, 100);
lv_slider_set_value(slider, 50, LV_ANIM_OFF);
*/
} }
void loop(){ void loop(){
wifi_ok(); wifi_ok();
data_loop(); data_loop();
lv_timer_handler(); lv_handler();
lv_task_handler(); serial_console::run();
if (is_ready_for_ota_update())
{
ota_do_update();
}
} }

View File

@@ -1,35 +1,181 @@
#include "ip_setup.h" #include "ip_setup.h"
#include "../conf/global_config.h" #include "../conf/global_config.h"
#include "lvgl.h" #include "lvgl.h"
#include <TFT_eSPI.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include "core/data_setup.h"
#include "ui_utils.h"
#include "panels/panel.h"
#include "macros.h"
#include "../core/lv_setup.h"
#include "serial/serial_console.h"
#include "../core/klipper/klipper_printer_integration.hpp"
#include "../core/bambu/bambu_printer_integration.hpp"
#include "../core/screen_driver.h"
#include "../core/klipper-serial/serial_klipper_printer_integration.hpp"
#include "../core/octoprint/octoprint_printer_integration.hpp"
bool connect_ok = false; void show_ip_entry();
lv_obj_t * ipEntry; void choose_printer_type();
lv_obj_t * portEntry;
lv_obj_t * label = NULL;
bool verify_ip(){ lv_obj_t * main_label;
HTTPClient client;
String url = "http://" + String(global_config.klipperHost) + ":" + String(global_config.klipperPort) + "/printer/info"; /* Create a custom keyboard to allow hostnames or ip addresses (a-z, 0 - 9, and -) */
int httpCode; static const char * kb_map[] = {
try { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
Serial.println(url); "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "\n",
client.begin(url.c_str()); "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_OK, "\n",
httpCode = client.GET(); LV_SYMBOL_LEFT, "z", "x", "c", "v", "b", "n", "m", ".", "-", LV_SYMBOL_RIGHT, NULL
return httpCode == 200; };
static const lv_btnmatrix_ctrl_t kb_ctrl[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_KEYBOARD_CTRL_BTN_FLAGS | 6,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, LV_KEYBOARD_CTRL_BTN_FLAGS | 5,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_KEYBOARD_CTRL_BTN_FLAGS | 6
};
static const char * hex_numpad_map[] = {
"1", "2", "3", "F", LV_SYMBOL_BACKSPACE, "\n",
"4", "5", "6", "E", LV_SYMBOL_OK, "\n",
"7", "8", "9", "D", LV_SYMBOL_LEFT, "\n",
"0", "A", "B", "C", LV_SYMBOL_RIGHT, NULL
};
static const lv_btnmatrix_ctrl_t hex_numpad_ctrl[] = {
1, 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 1,
1, 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 1,
1, 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 1,
1, 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 1,
};
static void btn_switch_printer(lv_event_t *e){
lv_obj_t *btn = lv_event_get_target(e);
PrinterConfiguration * config = (PrinterConfiguration*)lv_event_get_user_data(e);
int index = config - global_config.printer_config;
global_config_set_printer(index);
set_color_scheme();
set_invert_display();
lv_obj_del(lv_obj_get_parent(lv_obj_get_parent(btn)));
}
long last_request = 0;
void serial_check_connection()
{
if ((millis() - last_request) < 5000)
{
return;
} }
catch (...) {
Serial.println("Failed to connect"); auto result = connection_test_serial_klipper(&global_config.printer_config[global_config.printer_index]);
return false; last_request = millis();
if (result == KlipperConnectionStatus::ConnectOk)
{
global_config.printer_config[global_config.printer_index].setup_complete = true;
strcpy(global_config.printer_config[global_config.printer_index].printer_host, "Serial");
write_global_config();
} }
} }
static void ta_event_cb(lv_event_t * e) { void switch_printer_init() {
lv_obj_t * parent = lv_create_empty_panel(lv_scr_act());
lv_obj_set_style_bg_opa(parent, LV_OPA_100, 0);
lv_obj_align(parent, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_size(parent, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(parent);
lv_obj_set_size(lv_create_empty_panel(parent), 0, 0);
auto width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * btn = lv_btn_create(parent);
lv_obj_set_size(btn, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, parent);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE " Close");
lv_obj_center(label);
for (int i = 0; i < PRINTER_CONFIG_COUNT; i++){
PrinterConfiguration * config = &global_config.printer_config[i];
const char* printer_name = (config->printer_name[0] == 0) ? config->printer_host : config->printer_name;
if (i == global_config.printer_index && config->setup_complete)
{
lv_create_custom_menu_label(printer_name, parent, "Active");
continue;
}
if (config->setup_complete) {
lv_create_custom_menu_button(printer_name, parent, btn_switch_printer, "Switch", config);
}
}
}
static void show_switch_printer_screen(lv_event_t * e){
switch_printer_init();
}
static void host_update(lv_event_t * e)
{
lv_obj_t * ta = lv_event_get_target(e);
const char* text = lv_textarea_get_text(ta);
strcpy(global_config.printer_config[global_config.printer_index].printer_host, text);
global_config.printer_config[global_config.printer_index].ip_configured = text[0] != '\0';
}
static void port_update(lv_event_t * e)
{
lv_obj_t * ta = lv_event_get_target(e);
const char* text = lv_textarea_get_text(ta);
if (text[0] != '\0')
{
global_config.printer_config[global_config.printer_index].klipper_port = atoi(text);
}
}
static void auth_update(lv_event_t * e)
{
lv_obj_t * ta = lv_event_get_target(e);
const char* text = lv_textarea_get_text(ta);
strcpy(global_config.printer_config[global_config.printer_index].printer_auth, text);
global_config.printer_config[global_config.printer_index].auth_configured = text[0] != '\0';
}
static void return_to_choose_printer_type(lv_event_t * e)
{
choose_printer_type();
}
static void keyboard_event_ip_entry(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e); lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e); lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e); lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
if ((code == LV_EVENT_FOCUSED || code == LV_EVENT_DEFOCUSED) && ta != NULL)
{
// make sure we alter the keymap before taking actions that might
// destroy the keyboard
if (lv_obj_has_flag(ta, LV_OBJ_FLAG_USER_1))
{
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_1);
}
else if (lv_obj_has_flag(ta, LV_OBJ_FLAG_USER_2))
{
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_2);
}
else if (lv_obj_has_flag(ta, LV_OBJ_FLAG_USER_3))
{
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_TEXT_LOWER);
}
else
{
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER);
}
}
if(code == LV_EVENT_FOCUSED) { if(code == LV_EVENT_FOCUSED) {
lv_keyboard_set_textarea(kb, ta); lv_keyboard_set_textarea(kb, ta);
lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
@@ -40,62 +186,321 @@ static void ta_event_cb(lv_event_t * e) {
} }
else if (code == LV_EVENT_READY) else if (code == LV_EVENT_READY)
{ {
strcpy(global_config.klipperHost, lv_textarea_get_text(ipEntry)); PrinterType type = global_config.printer_config[global_config.printer_index].printer_type;
global_config.klipperPort = atoi(lv_textarea_get_text(portEntry));
bool result = verify_ip(); if (type == PrinterType::PrinterTypeKlipper)
if (result)
{ {
global_config.ipConfigured = true; KlipperConnectionStatus klipper_status = connection_test_klipper(&global_config.printer_config[global_config.printer_index]);
WriteGlobalConfig(); if (klipper_status == KlipperConnectionStatus::ConnectOk)
connect_ok = true; {
global_config.printer_config[global_config.printer_index].setup_complete = true;
write_global_config();
}
else if (klipper_status == KlipperConnectionStatus::ConnectAuthRequired)
{
lv_label_set_text(main_label, "Incorrect authorisation");
}
else
{
lv_label_set_text(main_label, "Failed to connect");
}
}
else if (type == PrinterType::PrinterTypeBambuLocal)
{
BambuConnectionStatus bambu_status = connection_test_bambu(&global_config.printer_config[global_config.printer_index]);
if (bambu_status == BambuConnectionStatus::BambuConnectOk)
{
global_config.printer_config[global_config.printer_index].setup_complete = true;
write_global_config();
}
else if (bambu_status == BambuConnectionStatus::BambuConnectSNFail)
{
lv_label_set_text(main_label, "Incorrect serial number");
}
else
{
lv_label_set_text(main_label, "Incorrect IP/Access code");
}
}
else if (type == PrinterType::PrinterTypeOctoprint)
{
OctoConnectionStatus octo_status = connection_test_octoprint(&global_config.printer_config[global_config.printer_index]);
if (octo_status == OctoConnectionStatus::OctoConnectOk)
{
global_config.printer_config[global_config.printer_index].setup_complete = true;
write_global_config();
}
else if (octo_status == OctoConnectionStatus::OctoConnectKeyFail)
{
lv_label_set_text(main_label, "Incorrect API key");
}
else
{
lv_label_set_text(main_label, "Failed to connect");
}
}
}
}
void show_ip_entry()
{
lv_obj_clean(lv_scr_act());
lv_obj_t * root = lv_create_empty_panel(lv_scr_act());
lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(root);
lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t * top_root = lv_create_empty_panel(root);
lv_obj_set_width(top_root, CYD_SCREEN_WIDTH_PX);
lv_layout_flex_column(top_root);
lv_obj_set_flex_grow(top_root, 1);
lv_obj_set_style_pad_all(top_root, CYD_SCREEN_GAP_PX, 0);
lv_obj_clear_flag(top_root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t * button_row = lv_create_empty_panel(top_root);
lv_obj_set_size(button_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_layout_flex_row(button_row);
lv_obj_t * button_back = lv_btn_create(button_row);
lv_obj_set_height(button_back, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2);
lv_obj_set_flex_grow(button_back, 1);
lv_obj_add_event_cb(button_back, return_to_choose_printer_type, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(button_back);
lv_label_set_text(label, LV_SYMBOL_LEFT);
lv_obj_center(label);
main_label = lv_label_create(button_row);
if (global_config.multi_printer_mode)
{
lv_obj_t * button_switch_printer = lv_btn_create(button_row);
lv_obj_set_height(button_switch_printer, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2);
lv_obj_set_flex_grow(button_switch_printer, 1);
lv_obj_add_event_cb(button_switch_printer, show_switch_printer_screen, LV_EVENT_CLICKED, NULL);
label = lv_label_create(button_switch_printer);
lv_label_set_text(label, LV_SYMBOL_HOME);
lv_obj_center(label);
}
lv_obj_t * ip_row = lv_create_empty_panel(top_root);
lv_obj_set_size(ip_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_layout_flex_row(ip_row);
lv_obj_t * host_entry = lv_textarea_create(ip_row);
lv_textarea_set_one_line(host_entry, true);
lv_obj_add_flag(host_entry, LV_OBJ_FLAG_USER_1);
lv_textarea_set_max_length(host_entry, 64);
lv_obj_set_flex_grow(host_entry, 2);
lv_obj_t * port_entry = lv_textarea_create(ip_row);
lv_textarea_set_one_line(port_entry, true);
lv_obj_set_flex_grow(port_entry, 1);
lv_obj_t * auth_row = lv_create_empty_panel(top_root);
lv_obj_set_size(auth_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_layout_flex_row(auth_row);
lv_obj_t * auth_entry = lv_textarea_create(auth_row);
lv_textarea_set_one_line(auth_entry, true);
lv_obj_add_flag(auth_entry, LV_OBJ_FLAG_USER_2);
lv_obj_set_flex_grow(auth_entry, 1);
lv_textarea_set_max_length(auth_entry, 32);
if (global_config.printer_config[global_config.printer_index].ip_configured)
{
char buff[10] = {0};
sprintf(buff, "%d", global_config.printer_config[global_config.printer_index].klipper_port);
lv_textarea_set_text(host_entry, global_config.printer_config[global_config.printer_index].printer_host);
lv_textarea_set_text(port_entry, buff);
}
else
{
lv_textarea_set_text(host_entry, "");
if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeBambuLocal)
{
lv_textarea_set_text(port_entry, "");
global_config.printer_config[global_config.printer_index].klipper_port = 0;
} }
else else
{ {
lv_label_set_text(label, "Failed to connect"); lv_textarea_set_text(port_entry, "80");
global_config.printer_config[global_config.printer_index].klipper_port = 80;
} }
global_config.printer_config[global_config.printer_index].printer_host[0] = '\0';
}
if (global_config.printer_config[global_config.printer_index].auth_configured)
{
lv_textarea_set_text(auth_entry, global_config.printer_config[global_config.printer_index].printer_auth);
}
else
{
lv_textarea_set_text(auth_entry, "");
global_config.printer_config[global_config.printer_index].printer_auth[0] = '\0';
}
lv_obj_add_event_cb(host_entry, host_update, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(port_entry, port_update, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(auth_entry, auth_update, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_t * keyboard = lv_keyboard_create(root);
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_1, kb_map, kb_ctrl);
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_2, hex_numpad_map, hex_numpad_ctrl);
lv_obj_add_event_cb(host_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard);
lv_obj_add_event_cb(port_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard);
lv_obj_add_event_cb(auth_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard);
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_1);
lv_keyboard_set_textarea(keyboard, host_entry);
switch (global_config.printer_config[global_config.printer_index].printer_type)
{
case PrinterType::PrinterTypeKlipper:
lv_label_set_text(main_label, "Klipper Setup");
lv_textarea_set_max_length(port_entry, 5);
lv_textarea_set_placeholder_text(host_entry, "Klipper host");
lv_textarea_set_placeholder_text(port_entry, "Port");
lv_textarea_set_placeholder_text(auth_entry, "Autorisation key (optional)");
break;
case PrinterType::PrinterTypeBambuLocal:
lv_label_set_text(main_label, "Bambu (Local) Setup");
lv_obj_set_flex_grow(port_entry, 4);
lv_obj_set_flex_grow(host_entry, 6);
lv_textarea_set_max_length(port_entry, 8);
lv_textarea_set_placeholder_text(host_entry, "Printer IP");
lv_textarea_set_placeholder_text(port_entry, "Access code");
lv_textarea_set_placeholder_text(auth_entry, "Printer serial number");
lv_obj_clear_flag(auth_entry, LV_OBJ_FLAG_USER_2);
lv_obj_add_flag(auth_entry, LV_OBJ_FLAG_USER_3);
break;
case PrinterType::PrinterTypeOctoprint:
lv_obj_clear_flag(auth_entry, LV_OBJ_FLAG_USER_2);
lv_obj_add_flag(auth_entry, LV_OBJ_FLAG_USER_3);
lv_textarea_set_max_length(auth_entry, 48);
lv_label_set_text(main_label, "Octoprint Setup");
lv_textarea_set_max_length(port_entry, 5);
lv_textarea_set_placeholder_text(host_entry, "Octoprint Host");
lv_textarea_set_placeholder_text(port_entry, "Port");
lv_textarea_set_placeholder_text(auth_entry, "API key");
break;
case PrinterType::PrinterTypeKlipperSerial:
lv_label_set_text(main_label, "Klipper (Serial) Setup");
lv_obj_del(ip_row);
lv_obj_del(auth_row);
lv_obj_del(keyboard);
lv_obj_t * bottom_root = lv_create_empty_panel(top_root);
lv_obj_set_width(bottom_root, CYD_SCREEN_WIDTH_PX);
lv_obj_set_flex_grow(bottom_root, 1);
label = lv_label_create(bottom_root);
lv_obj_center(label);
lv_label_set_text(label, "Connect CYD-Klipper to a host\nrunning the CYD-Klipper server");
break;
} }
} }
void ip_setup_inner(){ static void printer_type_klipper(lv_event_t * e)
lv_obj_clean(lv_scr_act()); {
global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeKlipper;
lv_obj_t * keyboard = lv_keyboard_create(lv_scr_act()); show_ip_entry();
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Enter Klipper IP and Port");
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10 + 2);
ipEntry = lv_textarea_create(lv_scr_act());
lv_textarea_set_one_line(ipEntry, true);
lv_textarea_set_max_length(ipEntry, 63);
lv_textarea_set_text(ipEntry, "");
lv_obj_align(ipEntry, LV_ALIGN_TOP_LEFT, 10, 40);
lv_obj_add_event_cb(ipEntry, ta_event_cb, LV_EVENT_ALL, keyboard);
lv_obj_set_size(ipEntry, TFT_HEIGHT - 20 - 100, 60);
portEntry = lv_textarea_create(lv_scr_act());
lv_textarea_set_one_line(portEntry, true);
lv_textarea_set_max_length(portEntry, 5);
lv_textarea_set_text(portEntry, "80");
lv_obj_align(portEntry, LV_ALIGN_TOP_LEFT, TFT_HEIGHT - 20 - 80, 40);
lv_obj_add_event_cb(portEntry, ta_event_cb, LV_EVENT_ALL, keyboard);
lv_obj_set_size(portEntry, 90, 60);
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER);
lv_keyboard_set_textarea(keyboard, ipEntry);
} }
void ip_setup(){ static void printer_type_bambu_local(lv_event_t * e)
connect_ok = false; {
global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeBambuLocal;
show_ip_entry();
}
if (global_config.ipConfigured && verify_ip()){ static void printer_type_serial_klipper(lv_event_t * e)
{
global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeKlipperSerial;
show_ip_entry();
}
static void printer_type_octoprint(lv_event_t * e)
{
global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeOctoprint;
show_ip_entry();
}
static void return_to_wifi_configuration(lv_event_t * e)
{
global_config.wifi_configuration_skipped = false;
write_global_config();
ESP.restart();
}
static inline void create_printer_type_button(lv_obj_t * root, const char * label, lv_event_cb_t event, bool require_wifi = true)
{
if (require_wifi && !global_config.wifi_configured)
{
return; return;
} }
ip_setup_inner(); lv_obj_t * btn = lv_btn_create(root);
lv_obj_set_size(btn, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, event, LV_EVENT_CLICKED, NULL);
while (!connect_ok) lv_obj_t * label_obj = lv_label_create(btn);
lv_label_set_text(label_obj, label);
lv_obj_center(label_obj);
}
void choose_printer_type()
{
lv_obj_t * btn;
lv_obj_clean(lv_scr_act());
global_config.printer_config[global_config.printer_index].ip_configured = false;
global_config.printer_config[global_config.printer_index].auth_configured = false;
lv_obj_t * root = lv_create_empty_panel(lv_scr_act());
lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(root);
lv_obj_set_flex_grow(root, 1);
lv_obj_set_style_pad_all(root, CYD_SCREEN_GAP_PX, 0);
lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t * label = lv_label_create(root);
lv_label_set_text(label, "Choose printer type");
create_printer_type_button(root, "Klipper (Wifi)", printer_type_klipper);
create_printer_type_button(root, "Klipper (Serial/USB)", printer_type_serial_klipper, false);
create_printer_type_button(root, "Bambu (Wifi, Local) [BETA]", printer_type_bambu_local);
create_printer_type_button(root, "Octoprint (Wifi) [BETA]", printer_type_octoprint);
if (global_config.wifi_configuration_skipped)
{ {
lv_timer_handler(); create_printer_type_button(root, "Return to WiFi configuration", return_to_wifi_configuration, false);
lv_task_handler(); }
}
void ip_init(){
if (!global_config.printer_config[global_config.printer_index].setup_complete)
{
if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeNone)
{
choose_printer_type();
}
else
{
show_ip_entry();
}
}
while (!global_config.printer_config[global_config.printer_index].setup_complete)
{
if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeKlipperSerial)
{
serial_check_connection();
}
lv_handler();
serial_console::run();
} }
} }

View File

@@ -1 +1,3 @@
void ip_setup(); #pragma once
void ip_init();

View File

@@ -0,0 +1,114 @@
#include "macros.h"
#include "ui_utils.h"
#include <Esp.h>
#include "../core/current_printer.h"
#include "../core/data_setup.h"
#include "../core/semaphore.h"
typedef struct {
const char* power_device_name;
BasePrinter* printer;
} DoubleStorage;
static void macro_run(lv_event_t * e){
lv_obj_t * btn = lv_event_get_target(e);
const char* macro = (const char*)lv_event_get_user_data(e);
LOG_F(("Macro: %s\n", macro))
current_printer_execute_macro(macro);
}
int macros_add_macros_to_panel(lv_obj_t * root_panel, BasePrinter* printer)
{
freeze_request_thread();
Macros macros = printer->get_macros();
unfreeze_request_thread();
if (!macros.success)
{
return 0;
}
if (global_config.sort_macros)
{
std::sort(macros.macros, macros.macros + macros.count, [](const char *a, const char *b)
{ return strcmp(a, b) < 0; });
}
for (int i = 0; i < macros.count; i++)
{
const char* macro = macros.macros[i];
lv_obj_on_destroy_free_data(root_panel, macro);
lv_create_custom_menu_button(macro, root_panel, macro_run, "Run", (void*)macro);
}
free(macros.macros);
return macros.count;
}
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);
DoubleStorage* device = (DoubleStorage*)lv_event_get_user_data(e);
LOG_F(("Power Device: %s, State: %d -> %d\n", device->power_device_name, !checked, checked))
freeze_request_thread();
device->printer->set_power_device_state(device->power_device_name, checked);
unfreeze_request_thread();
}
int macros_add_power_devices_to_panel(lv_obj_t * root_panel, BasePrinter* printer)
{
freeze_request_thread();
PowerDevices devices = printer->get_power_devices();
unfreeze_request_thread();
if (!devices.success)
{
return 0;
}
for (int i = 0; i < devices.count; i++)
{
const char* power_device_name = devices.power_devices[i];
const bool power_device_state = devices.power_states[i];
DoubleStorage* storage = (DoubleStorage*)malloc(sizeof(DoubleStorage));
storage->printer = printer;
storage->power_device_name = power_device_name;
lv_obj_on_destroy_free_data(root_panel, storage);
lv_obj_on_destroy_free_data(root_panel, power_device_name);
lv_create_custom_menu_switch(power_device_name, root_panel, power_device_toggle, power_device_state, (void*)storage);
}
free(devices.power_devices);
free(devices.power_states);
return devices.count;
}
void macros_draw_power_fullscreen(BasePrinter* printer)
{
lv_obj_t * parent = lv_create_empty_panel(lv_scr_act());
lv_obj_set_style_bg_opa(parent, LV_OPA_100, 0);
lv_obj_align(parent, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_size(parent, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(parent);
lv_obj_set_size(lv_create_empty_panel(parent), 0, 0);
auto width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * btn = lv_btn_create(parent);
lv_obj_set_size(btn, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, parent);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE " Close");
lv_obj_center(label);
macros_add_power_devices_to_panel(parent, printer);
}
void macros_draw_power_fullscreen()
{
macros_draw_power_fullscreen(get_current_printer());
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "lvgl.h"
#include "../conf/global_config.h"
#include "../core/printer_integration.hpp"
int macros_add_macros_to_panel(lv_obj_t * root_panel, BasePrinter* printer);
int macros_add_power_devices_to_panel(lv_obj_t * root_panel, BasePrinter* printer);
void macros_draw_power_fullscreen(BasePrinter* printer);
void macros_draw_power_fullscreen();

View File

@@ -2,57 +2,16 @@
#include "../core/data_setup.h" #include "../core/data_setup.h"
#include "../conf/global_config.h" #include "../conf/global_config.h"
#include "../core/screen_driver.h" #include "../core/screen_driver.h"
#include "../core/printer_integration.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "nav_buttons.h" #include "nav_buttons.h"
#include "ui_utils.h"
char extruder_temp_buff[20]; #include "panels/panel.h"
char bed_temp_buff[20]; #include "../core/lv_setup.h"
char position_buff[20]; #include "macros.h"
static void btn_click_restart(lv_event_t * e){
send_gcode(false, "RESTART");
}
static void btn_click_firmware_restart(lv_event_t * e){
send_gcode(false, "FIRMWARE_RESTART");
}
void error_ui(){
lv_obj_clean(lv_scr_act());
lv_obj_t * label;
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, LV_SYMBOL_WARNING " Printer is not ready");
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10);
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, printer.state_message);
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 30);
lv_obj_set_size(label, TFT_HEIGHT - 20, TFT_WIDTH - 30);
lv_obj_clear_flag(label, LV_OBJ_FLAG_SCROLLABLE);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 10, -10);
lv_obj_set_size(btn, TFT_HEIGHT / 2 - 15, 30);
lv_obj_add_event_cb(btn, btn_click_restart, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "Restart");
lv_obj_center(label);
btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -10, -10);
lv_obj_set_size(btn, TFT_HEIGHT / 2 - 15, 30);
lv_obj_add_event_cb(btn, btn_click_firmware_restart, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "Firmware Restart");
lv_obj_center(label);
}
void check_if_screen_needs_to_be_disabled(){ void check_if_screen_needs_to_be_disabled(){
if (global_config.onDuringPrint && printer.state == PRINTER_STATE_PRINTING){ if (global_config.on_during_print && get_current_printer_data()->state == PrinterState::PrinterStatePrinting){
screen_timer_wake(); screen_timer_wake();
screen_timer_stop(); screen_timer_stop();
} }
@@ -64,16 +23,29 @@ void check_if_screen_needs_to_be_disabled(){
static void on_state_change(void * s, lv_msg_t * m){ static void on_state_change(void * s, lv_msg_t * m){
check_if_screen_needs_to_be_disabled(); check_if_screen_needs_to_be_disabled();
if (printer.state == PRINTER_STATE_ERROR){ PrinterData* printer = get_current_printer_data();
error_ui();
if (printer->state == PrinterState::PrinterStateOffline){
nav_buttons_setup(PANEL_CONNECTING);
}
else if (printer->state == PrinterState::PrinterStateError){
nav_buttons_setup(PANEL_ERROR);
}
else if (printer->state == PrinterState::PrinterStateIdle) {
nav_buttons_setup(PANEL_FILES);
} }
else { else {
nav_buttons_setup(0); nav_buttons_setup(PANEL_PROGRESS);
} }
} }
static void on_popup_message(void * s, lv_msg_t * m)
{
lv_create_popup_message(get_current_printer_data()->popup_message, get_current_printer()->popup_message_timeout_s * 1000);
}
void main_ui_setup(){ void main_ui_setup(){
lv_msg_subscribe(DATA_PRINTER_STATE, on_state_change, NULL); lv_msg_subscribe(DATA_PRINTER_STATE, on_state_change, NULL);
lv_msg_subscribe(DATA_PRINTER_POPUP, on_popup_message, NULL);
on_state_change(NULL, NULL); on_state_change(NULL, NULL);
} }

View File

@@ -1,45 +1,45 @@
#include "lvgl.h" #include "lvgl.h"
#include "panels/panel.h" #include "panels/panel.h"
#include "../core/data_setup.h"
#include "nav_buttons.h" #include "nav_buttons.h"
#include <HTTPClient.h> #include "ui_utils.h"
#include <stdio.h>
#include "../conf/global_config.h"
#include "../core/printer_integration.hpp"
static lv_style_t nav_button_style; static lv_style_t nav_button_style;
static char temp_buffer[10];
static char z_pos_buffer[10];
static char time_buffer[10];
static lv_style_t nav_button_text_style; static lv_style_t nav_button_text_style;
static void update_printer_data_z_pos(lv_event_t * e) { static void update_printer_data_z_pos(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
char z_pos_buffer[10];
sprintf(z_pos_buffer, "Z%.2f", printer.position[2]); sprintf(z_pos_buffer, "Z%.2f", get_current_printer_data()->position[2]);
lv_label_set_text(label, z_pos_buffer); lv_label_set_text(label, z_pos_buffer);
} }
static void update_printer_data_temp(lv_event_t * e) { static void update_printer_data_temp(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
char temp_buffer[10];
sprintf(temp_buffer, "%.0f/%.0f", printer.extruder_temp, printer.bed_temp); sprintf(temp_buffer, "%.0f/%.0f", get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1], get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
lv_label_set_text(label, temp_buffer); lv_label_set_text(label, temp_buffer);
} }
static void update_printer_data_time(lv_event_t * e){ static void update_printer_data_time(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
char time_buffer[10];
if (printer.state == PRINTER_STATE_IDLE){ if (get_current_printer_data()->state == PrinterState::PrinterStateIdle){
lv_label_set_text(label, "Idle"); lv_label_set_text(label, "Idle");
return; return;
} }
if (printer.state == PRINTER_STATE_PAUSED){ if (get_current_printer_data()->state == PrinterState::PrinterStatePaused){
lv_label_set_text(label, "Paused"); lv_label_set_text(label, "Paused");
return; return;
} }
unsigned long time = printer.remaining_time_s; unsigned long time = get_current_printer_data()->remaining_time_s;
unsigned long hours = time / 3600; unsigned long hours = time / 3600;
unsigned long minutes = (time % 3600) / 60; unsigned long minutes = (time % 3600) / 60;
unsigned long seconds = (time % 3600) % 60; unsigned long seconds = (time % 3600) % 60;
@@ -55,123 +55,181 @@ static void update_printer_data_time(lv_event_t * e){
lv_label_set_text(label, time_buffer); lv_label_set_text(label, time_buffer);
} }
static void update_multi_printer_label(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
int idle_count = 0;
for (int i = 0; i < get_printer_count(); i++)
{
PrinterDataMinimal* data = get_printer_data_minimal(i);
if (data->state == PrinterState::PrinterStateIdle)
{
idle_count++;
}
}
if (idle_count > 0)
{
lv_label_set_text_fmt(label, "%d idle", idle_count);
}
else
{
lv_label_set_text(label, "Printer");
}
}
static void btn_click_files(lv_event_t * e){ static void btn_click_files(lv_event_t * e){
nav_buttons_setup(0); nav_buttons_setup(PANEL_FILES);
}
static void btn_click_progress(lv_event_t * e){
nav_buttons_setup(PANEL_PROGRESS);
} }
static void btn_click_move(lv_event_t * e){ static void btn_click_move(lv_event_t * e){
nav_buttons_setup(1); nav_buttons_setup(PANEL_MOVE);
} }
static void btn_click_extrude(lv_event_t * e){ static void btn_click_extrude(lv_event_t * e){
nav_buttons_setup(2); nav_buttons_setup(PANEL_TEMP);
} }
static void btn_click_settings(lv_event_t * e){ static void btn_click_settings(lv_event_t * e){
nav_buttons_setup(3); nav_buttons_setup(PANEL_SETTINGS);
} }
void nav_buttons_setup(unsigned char active_panel){ static void btn_click_macros(lv_event_t * e){
lv_obj_clean(lv_scr_act()); nav_buttons_setup(PANEL_MACROS);
lv_obj_clear_flag(lv_scr_act(), LV_OBJ_FLAG_SCROLLABLE); }
sprintf(temp_buffer, "%.0f/%.0f", printer.extruder_temp, printer.bed_temp);
sprintf(z_pos_buffer, "Z%.2f", printer.position[2]);
const int button_width = 40; static void btn_click_printer(lv_event_t * e){
const int button_height = 60; nav_buttons_setup(PANEL_PRINTER);
const int icon_text_spacing = 10; }
static void btn_click_err(lv_event_t * e){
nav_buttons_setup(PANEL_ERROR);
}
static void btn_click_conn(lv_event_t * e){
nav_buttons_setup(PANEL_CONNECTING);
}
void create_button(const char* icon, const char* name, lv_event_cb_t button_click, lv_event_cb_t label_update, lv_obj_t * root){
lv_obj_t* btn = lv_btn_create(root);
lv_obj_set_flex_grow(btn, 1);
#ifdef CYD_SCREEN_VERTICAL
lv_obj_set_height(btn, CYD_SCREEN_SIDEBAR_SIZE_PX);
#else
lv_obj_set_width(btn, CYD_SCREEN_SIDEBAR_SIZE_PX);
#endif
// Files/Print
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, button_width, button_height);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_add_style(btn, &nav_button_style, 0); lv_obj_add_style(btn, &nav_button_style, 0);
lv_obj_add_event_cb(btn, btn_click_files, LV_EVENT_CLICKED, NULL); if (button_click != NULL)
lv_obj_add_event_cb(btn, button_click, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(btn); lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_COPY); lv_label_set_text(label, icon);
lv_obj_align(label, LV_ALIGN_CENTER, 0, -1 * icon_text_spacing); lv_obj_align(label, LV_ALIGN_CENTER, 0, -1 * CYD_SCREEN_GAP_PX);
label = lv_label_create(btn);
lv_label_set_text(label, "Idle");
lv_obj_align(label, LV_ALIGN_CENTER, 0, icon_text_spacing);
lv_obj_add_style(label, &nav_button_text_style, 0);
lv_obj_add_event_cb(label, update_printer_data_time, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
// Move
btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, button_width, button_height);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, button_height);
lv_obj_add_style(btn, &nav_button_style, 0);
lv_obj_add_event_cb(btn, btn_click_move, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CHARGE);
lv_obj_align(label, LV_ALIGN_CENTER, 0, -1 * icon_text_spacing);
label = lv_label_create(btn); label = lv_label_create(btn);
lv_label_set_text(label, z_pos_buffer); lv_label_set_text(label, name);
lv_obj_align(label, LV_ALIGN_CENTER, 0, icon_text_spacing); lv_obj_align(label, LV_ALIGN_CENTER, 0, CYD_SCREEN_GAP_PX);
lv_obj_add_event_cb(label, update_printer_data_z_pos, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(label, label_update, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL); lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_add_style(label, &nav_button_text_style, 0); lv_obj_add_style(label, &nav_button_text_style, 0);
}
// Extrude/Temp void nav_buttons_setup(PANEL_TYPE active_panel){
btn = lv_btn_create(lv_scr_act()); lv_obj_clean(lv_scr_act());
lv_obj_set_size(btn, button_width, button_height); lv_obj_clear_flag(lv_scr_act(), LV_OBJ_FLAG_SCROLLABLE);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, button_height * 2);
lv_obj_add_style(btn, &nav_button_style, 0);
lv_obj_add_event_cb(btn, btn_click_extrude, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn); lv_obj_t * root_panel = lv_create_empty_panel(lv_scr_act());
lv_label_set_text(label, LV_SYMBOL_WARNING);
lv_obj_align(label, LV_ALIGN_CENTER, 0, -1 * icon_text_spacing);
label = lv_label_create(btn); #ifdef CYD_SCREEN_VERTICAL
lv_label_set_text(label, temp_buffer); lv_obj_set_size(root_panel, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_SIDEBAR_SIZE_PX);
lv_obj_align(label, LV_ALIGN_CENTER, 0, icon_text_spacing); lv_obj_align(root_panel, LV_ALIGN_BOTTOM_LEFT, 0, 0);
lv_obj_add_event_cb(label, update_printer_data_temp, LV_EVENT_MSG_RECEIVED, NULL); lv_layout_flex_row(root_panel, LV_FLEX_ALIGN_START, 0, 0);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL); #else
lv_obj_add_style(label, &nav_button_text_style, 0); lv_obj_set_size(root_panel, CYD_SCREEN_SIDEBAR_SIZE_PX, CYD_SCREEN_HEIGHT_PX);
lv_obj_align(root_panel, LV_ALIGN_TOP_LEFT, 0, 0);
lv_layout_flex_column(root_panel, LV_FLEX_ALIGN_START, 0, 0);
// Settings #endif
btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, button_width, button_height);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, button_height * 3);
lv_obj_add_style(btn, &nav_button_style, 0);
lv_obj_add_event_cb(btn, btn_click_settings, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn); if (get_current_printer_data()->state > PrinterState::PrinterStateError){
lv_label_set_text(label, LV_SYMBOL_SETTINGS); // Files/Print
lv_obj_align(label, LV_ALIGN_CENTER, 0, -1 * icon_text_spacing); if (get_current_printer_data()->state == PrinterState::PrinterStateIdle)
{
create_button(LV_SYMBOL_COPY, "Idle", btn_click_files, update_printer_data_time, root_panel);
}
else
{
create_button(LV_SYMBOL_FILE, "Paused", btn_click_progress, update_printer_data_time, root_panel);
}
label = lv_label_create(btn); // Move
lv_label_set_text(label, "Screen"); create_button(get_current_printer_data()->state == PrinterState::PrinterStatePrinting ? LV_SYMBOL_EDIT : LV_SYMBOL_CHARGE, "Z?", btn_click_move, update_printer_data_z_pos, root_panel);
lv_obj_align(label, LV_ALIGN_CENTER, 0, icon_text_spacing);
lv_obj_add_style(label, &nav_button_text_style, 0);
lv_obj_t * panel = lv_obj_create(lv_scr_act()); // Extrude/Temp
lv_obj_set_size(panel, TFT_HEIGHT - button_width, TFT_WIDTH); create_button(LV_SYMBOL_WARNING, "?/?", btn_click_extrude, update_printer_data_temp, root_panel);
}
else if (get_current_printer_data()->state == PrinterState::PrinterStateError) {
// Error UI
create_button(LV_SYMBOL_WARNING, "Error", btn_click_err, NULL, root_panel);
}
else {
// Connecting
create_button(LV_SYMBOL_REFRESH, "Link", btn_click_conn, NULL, root_panel);
}
// Macros
create_button(LV_SYMBOL_GPS, "Macro", btn_click_macros, NULL, root_panel);
if (global_config.multi_printer_mode)
{
// Printers
create_button(LV_SYMBOL_HOME, "Printer", btn_click_printer, update_multi_printer_label, root_panel);
}
lv_obj_t * panel = lv_create_empty_panel(lv_scr_act());
lv_obj_set_size(panel, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_obj_align(panel, LV_ALIGN_TOP_RIGHT, 0, 0); lv_obj_align(panel, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_border_width(panel, 0, 0);
lv_obj_set_style_bg_opa(panel, LV_OPA_TRANSP, 0);
lv_obj_set_style_pad_all(panel, 0, 0);
switch (active_panel){ switch (active_panel){
case 0: case PANEL_FILES:
print_panel_init(panel); files_panel_init(panel);
break; break;
case 1: case PANEL_MOVE:
move_panel_init(panel); move_panel_init(panel);
break; break;
case 2: case PANEL_TEMP:
temp_panel_init(panel); temp_panel_init(panel);
break; break;
case 3: case PANEL_SETTINGS:
settings_panel_init(panel); settings_panel_init(panel);
break; break;
case PANEL_MACROS:
macros_panel_init(panel);
break;
case PANEL_STATS:
stats_panel_init(panel);
break;
case PANEL_PRINTER:
printer_panel_init(panel);
break;
case PANEL_ERROR:
error_panel_init(panel);
break;
case PANEL_CONNECTING:
connecting_panel_init(panel);
break;
case PANEL_PROGRESS:
progress_panel_init(panel);
break;
} }
lv_msg_send(DATA_PRINTER_DATA, get_current_printer());
} }
void nav_style_setup(){ void nav_style_setup(){
@@ -179,5 +237,5 @@ void nav_style_setup(){
lv_style_set_radius(&nav_button_style, 0); lv_style_set_radius(&nav_button_style, 0);
lv_style_init(&nav_button_text_style); lv_style_init(&nav_button_text_style);
lv_style_set_text_font(&nav_button_text_style, &lv_font_montserrat_10); lv_style_set_text_font(&nav_button_text_style, &CYD_SCREEN_FONT_SMALL);
} }

View File

@@ -1,2 +1,17 @@
void nav_buttons_setup(unsigned char active_panel); #pragma once
enum PANEL_TYPE {
PANEL_FILES = 0,
PANEL_MOVE = 1,
PANEL_TEMP = 2,
PANEL_SETTINGS = 3,
PANEL_MACROS = 4,
PANEL_STATS = 5,
PANEL_PRINTER = 6,
PANEL_ERROR = 7,
PANEL_CONNECTING = 8,
PANEL_PROGRESS = 9,
};
void nav_buttons_setup(PANEL_TYPE active_panel);
void nav_style_setup(); void nav_style_setup();

View File

@@ -0,0 +1,104 @@
#include "../lib/ESP32OTAPull.h"
#include "lvgl.h"
#include "ui_utils.h"
#include "../core/lv_setup.h"
#include "../core/data_setup.h"
#include "../conf/global_config.h"
#include "ota_setup.h"
#include "../core/semaphore.h"
//const char *ota_url = "https://gist.githubusercontent.com/suchmememanyskill/ece418fe199e155340de6c224a0badf2/raw/0d6762d68bc807cbecc71e40d55b76692397a7b3/update.json"; // Test url
const char *ota_url = "https://suchmememanyskill.github.io/CYD-Klipper/OTA.json"; // Prod url
ESP32OTAPull ota_pull;
static bool update_available;
static bool ready_for_ota_update = false;
String ota_new_version_name()
{
return ota_pull.GetVersion();
}
bool ota_has_update()
{
return update_available;
}
static int last_callback_time = 0;
lv_obj_t *percentage_bar;
lv_obj_t *update_label;
void do_update_callback(int offset, int totallength)
{
int now = millis();
if (now - last_callback_time < 1000)
{
return;
}
last_callback_time = now;
float percentage = (float)offset / (float)totallength; // 0 -> 1
lv_bar_set_value(percentage_bar, percentage * 100, LV_ANIM_OFF);
lv_label_set_text_fmt(update_label, "%d/%d bytes", offset, totallength);
lv_refr_now(NULL);
lv_timer_handler();
lv_task_handler();
}
void ota_do_update(bool variant_automatic)
{
LOG_LN("Starting OTA Update");
lv_obj_clean(lv_scr_act());
lv_obj_t *panel = lv_create_empty_panel(lv_scr_act());
lv_obj_set_size(panel, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_obj_align(panel, LV_ALIGN_TOP_LEFT, 0, 0);
lv_layout_flex_column(panel, LV_FLEX_ALIGN_CENTER);
lv_obj_t *label = lv_label_create(panel);
lv_label_set_text(label, "Updating OTA...");
percentage_bar = lv_bar_create(panel);
lv_obj_set_size(percentage_bar, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX * 0.75f);
update_label = lv_label_create(panel);
lv_label_set_text(update_label, "0/0");
if (!variant_automatic) {
LOG_LN("Freezing Background Tasks");
screen_timer_wake();
screen_timer_stop();
freeze_request_thread();
}
lv_refr_now(NULL);
lv_timer_handler();
lv_task_handler();
ota_pull.SetCallback(do_update_callback);
ota_pull.CheckForOTAUpdate(ota_url, REPO_VERSION, ESP32OTAPull::ActionType::UPDATE_AND_BOOT);
}
void ota_init()
{
//ota_pull.AllowDowngrades(true);
int result = ota_pull.CheckForOTAUpdate(ota_url, REPO_VERSION, ESP32OTAPull::ActionType::DONT_DO_UPDATE);
LOG_F(("OTA Update Result: %d\n", result))
update_available = result == ESP32OTAPull::UPDATE_AVAILABLE;
if (global_config.auto_ota_update && update_available)
{
ota_do_update(true);
}
}
void set_ready_for_ota_update()
{
ready_for_ota_update = true;
}
bool is_ready_for_ota_update()
{
return ready_for_ota_update;
}

View File

@@ -0,0 +1,8 @@
#pragma once
String ota_new_version_name();
bool ota_has_update();
void ota_do_update(bool variant_automatic = false);
void ota_init();
void set_ready_for_ota_update();
bool is_ready_for_ota_update();

View File

@@ -0,0 +1,11 @@
#include "panel.h"
#include "../../core/printer_integration.hpp"
void connecting_panel_init(lv_obj_t* panel)
{
lv_obj_t* label = lv_label_create(panel);
lv_label_set_text_fmt(label, "Connecting to %s...", (get_current_printer()->printer_config->printer_name[0] == 0)
? get_current_printer()->printer_config->printer_host
: get_current_printer()->printer_config->printer_name);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

View File

@@ -0,0 +1,90 @@
#include "panel.h"
#include "../../core/current_printer.h"
#include "../ui_utils.h"
#include "../../core/printer_integration.hpp"
static void btn_click_restart(lv_event_t * e){
current_printer_execute_feature(PrinterFeatureRestart);
}
static void btn_click_firmware_restart(lv_event_t * e){
current_printer_execute_feature(PrinterFeatureFirmwareRestart);
}
static void btn_click_error_ignore(lv_event_t * e){
current_printer_execute_feature(PrinterFeatureIgnoreError);
}
static void btn_click_error_continue(lv_event_t * e){
current_printer_execute_feature(PrinterFeatureContinueError);
}
static void btn_click_error_retry(lv_event_t * e){
current_printer_execute_feature(PrinterFeatureRetryError);
}
static void set_state_message_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, get_current_printer_data()->state_message);
}
void create_button(const char* label, lv_event_cb_t on_click, lv_obj_t * root){
lv_obj_t* btn = lv_btn_create(root);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, on_click, LV_EVENT_CLICKED, NULL);
lv_obj_t * label_obj = lv_label_create(btn);
lv_label_set_text(label_obj, label);
lv_obj_center(label_obj);
}
void error_panel_init(lv_obj_t* panel)
{
lv_layout_flex_column(panel, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, 0);
lv_obj_t * label;
label = lv_label_create(panel);
lv_label_set_text(label, LV_SYMBOL_WARNING " Printer is not ready");
lv_obj_t * panel_with_text = lv_create_empty_panel(panel);
lv_layout_flex_column(panel_with_text, LV_FLEX_ALIGN_START);
lv_obj_set_flex_grow(panel_with_text, 1);
lv_obj_set_width(panel_with_text, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2);
label = lv_label_create(panel_with_text);
lv_obj_set_width(label, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_add_event_cb(label, set_state_message_text, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_t * button_row = lv_create_empty_panel(panel);
lv_obj_set_size(button_row, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(button_row);
if (get_current_printer_data()->error_screen_features & PrinterFeatureRestart)
{
create_button("Restart", btn_click_restart, button_row);
}
if (get_current_printer_data()->error_screen_features & PrinterFeatureFirmwareRestart)
{
create_button("FW Restart", btn_click_firmware_restart, button_row);
}
if (get_current_printer_data()->error_screen_features & PrinterFeatureIgnoreError)
{
create_button("Ignore", btn_click_error_ignore, button_row);
}
if (get_current_printer_data()->error_screen_features & PrinterFeatureContinueError)
{
create_button("Done", btn_click_error_continue, button_row);
}
if (get_current_printer_data()->error_screen_features & PrinterFeatureRetryError)
{
create_button("Retry", btn_click_error_retry, button_row);
}
}

View File

@@ -0,0 +1,142 @@
#include "lvgl.h"
#include "panel.h"
#include "../../core/current_printer.h"
#include "../../conf/global_config.h"
#include <HardwareSerial.h>
#include "../ui_utils.h"
#include "../../core/lv_setup.h"
#include <UrlEncode.h>
#include "../../core/printer_integration.hpp"
const char* selected_file = NULL;
static void btn_print_file(lv_event_t * e)
{
lv_obj_t * panel = (lv_obj_t*)lv_event_get_user_data(e);
lv_obj_del(panel);
current_printer_start_file(selected_file);
}
static void btn_print_file_verify_instant(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
selected_file = (char*)lv_event_get_user_data(e);
current_printer_start_file(selected_file);
}
static void btn_print_file_verify(lv_event_t * e)
{
if (get_current_printer_data()->state != PrinterState::PrinterStateIdle){
return;
}
selected_file = (char*)lv_event_get_user_data(e);
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_style_pad_hor(panel, CYD_SCREEN_GAP_PX * 2, 0);
lv_obj_set_style_pad_ver(panel, CYD_SCREEN_GAP_PX, 0);
lv_obj_set_size(panel, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 4, CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX * 3);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 0);
lv_layout_flex_column(panel, LV_FLEX_ALIGN_CENTER);
lv_obj_t * label_print_file = lv_label_create(panel);
lv_label_set_text(label_print_file, "Print File");
lv_obj_t * top_panel = lv_create_empty_panel(panel);
lv_obj_set_width(top_panel, LV_PCT(100));
lv_obj_set_flex_grow(top_panel, 1);
lv_layout_flex_row(top_panel, LV_FLEX_ALIGN_CENTER);
Thumbnail thumbnail = current_printer_get_32_32_png_image_thumbnail(selected_file);
lv_obj_t * img = NULL;
if (thumbnail.success)
{
lv_img_dsc_t* img_header = (lv_img_dsc_t*)malloc(sizeof(lv_img_dsc_t));
lv_obj_on_destroy_free_data(panel, img_header);
memset(img_header, 0, sizeof(lv_img_dsc_t));
img_header->header.w = 32;
img_header->header.h = 32;
img_header->data_size = thumbnail.size;
img_header->header.cf = LV_IMG_CF_RAW_ALPHA;
img_header->data = thumbnail.png;
img = lv_img_create(top_panel);
lv_img_set_src(img, img_header);
lv_img_set_antialias(img, true);
lv_img_set_size_mode(img, LV_IMG_SIZE_MODE_REAL);
if (global_config.double_size_gcode_img)
{
lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * 2);
}
}
lv_obj_t * label = lv_label_create(top_panel);
lv_label_set_text(label, selected_file);
lv_obj_set_height(label, LV_SIZE_CONTENT);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_set_flex_grow(label, 1);
lv_obj_t* buttons_panel = lv_create_empty_panel(panel);
lv_layout_flex_row(buttons_panel);
lv_obj_set_size(buttons_panel, LV_PCT(100), CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t* btn = lv_btn_create(buttons_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE " Cancel");
lv_obj_center(label);
btn = lv_btn_create(buttons_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, btn_print_file, LV_EVENT_CLICKED, panel);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_OK " Print");
lv_obj_center(label);
}
void files_panel_init(lv_obj_t* panel){
Files files = current_printer_get_files();
if (!files.success || files.count <= 0){
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);
return;
}
lv_obj_t * list = lv_list_create(panel);
lv_obj_set_style_radius(list, 0, 0);
lv_obj_set_style_border_width(list, 0, 0);
lv_obj_set_style_bg_opa(list, LV_OPA_TRANSP, 0);
lv_obj_set_size(list, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_obj_align(list, LV_ALIGN_CENTER, 0, 0);
for (int i = 0; i < files.count; i++)
{
lv_obj_t * btn = lv_list_add_btn(list, LV_SYMBOL_FILE, files.available_files[i]);
lv_obj_set_style_bg_opa(btn, LV_OPA_TRANSP, 0);
if (global_config.full_filenames)
{
lv_label_set_long_mode(lv_obj_get_child(btn, 1), LV_LABEL_LONG_WRAP);
}
lv_obj_add_event_cb(btn, (get_current_printer()->no_confirm_print_file) ? btn_print_file_verify_instant : btn_print_file_verify, LV_EVENT_CLICKED, (void*)(files.available_files[i]));
lv_obj_on_destroy_free_data(btn, files.available_files[i]);
}
// Not deallocating filenames in this scope will cause double allocation, oh well.
// TODO: read label text
free(files.available_files);
}

View File

@@ -0,0 +1,50 @@
#include "../macros.h"
#include "panel.h"
#include "../nav_buttons.h"
#include "../../conf/global_config.h"
#include "../ui_utils.h"
#include <HardwareSerial.h>
static void btn_goto_settings(lv_event_t * e){
nav_buttons_setup(PANEL_SETTINGS);
}
void macros_panel_init(lv_obj_t* panel) {
lv_obj_t * btn = lv_btn_create(panel);
lv_obj_add_event_cb(btn, btn_goto_settings, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, CYD_SCREEN_GAP_PX);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_SETTINGS " Screen Settings");
lv_obj_center(label);
lv_obj_t * root_panel = lv_create_empty_panel(panel);
lv_obj_set_scrollbar_mode(root_panel, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(root_panel, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX - CYD_SCREEN_MIN_BUTTON_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_obj_align(root_panel, LV_ALIGN_TOP_MID, 0, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX + CYD_SCREEN_GAP_PX * 2);
lv_layout_flex_column(root_panel);
int power_count = macros_add_power_devices_to_panel(root_panel, get_current_printer());
int macros_count = macros_add_macros_to_panel(root_panel, get_current_printer());
if (macros_count <= 0){
label = lv_label_create(root_panel);
if (get_current_printer()->printer_config->printer_type == PrinterType::PrinterTypeKlipper
|| get_current_printer()->printer_config->printer_type == PrinterType::PrinterTypeKlipperSerial)
{
lv_label_set_text(label, "No macros found.\nMacros with the description\n\"CYD_SCREEN_MACRO\"\nwill show up here.");
}
else
{
lv_label_set_text(label, "No macros found.");
}
if (power_count <= 0){
lv_layout_flex_column(root_panel, LV_FLEX_ALIGN_CENTER);
}
return;
}
}

View File

@@ -1,209 +1,369 @@
#include "lvgl.h" #include "lvgl.h"
#include "panel.h" #include "panel.h"
#include "../../core/data_setup.h" #include "../../core/data_setup.h"
#include <TFT_eSPI.h> #include "../nav_buttons.h"
#include "../ui_utils.h"
#include <stdio.h>
#include <Esp.h>
#include "../../core/printer_integration.hpp"
#include "../../core/current_printer.h"
static void move_printer(const char* axis, float amount) { static bool last_homing_state = false;
if (!printer.homed_axis || printer.state == PRINTER_STATE_PRINTING) static bool move_edit_mode = false;
float x_offsets[6] = {0};
float y_offsets[6] = {0};
float z_offsets[6] = {0};
#define OFFSET_LABEL_SIZE 7
char x_offset_labels[6 * OFFSET_LABEL_SIZE] = {0};
char y_offset_labels[6 * OFFSET_LABEL_SIZE] = {0};
char z_offset_labels[6 * OFFSET_LABEL_SIZE] = {0};
static void calculate_offsets_from_current_printer()
{
unsigned short* items[] = {get_current_printer()->printer_config->printer_move_x_steps, get_current_printer()->printer_config->printer_move_y_steps, get_current_printer()->printer_config->printer_move_z_steps};
float* offsets[] = {(float*)x_offsets, (float*)y_offsets, (float*)z_offsets};
char * labels[] = {(char*)x_offset_labels, (char*)y_offset_labels, (char*)z_offset_labels};
for (int i = 0; i < 3; i++)
{
offsets[i][0] = items[i][2] / 10.0f * -1;
offsets[i][1] = items[i][1] / 10.0f * -1;
offsets[i][2] = items[i][0] / 10.0f * -1;
offsets[i][3] = items[i][0] / 10.0f;
offsets[i][4] = items[i][1] / 10.0f;
offsets[i][5] = items[i][2] / 10.0f;
for (int j = 0; j < 6; j++) {
const char * formats[] = {"%.0f", "%.1f", "+%.0f", "+%.1f"};
const char ** format = formats;
if (offsets[i][j] != (int)offsets[i][j])
{
format += 1;
}
if (j >= 3)
{
format += 2;
}
sprintf(labels[i] + OFFSET_LABEL_SIZE * j, *format, offsets[i][j]);
}
}
}
static int selected_column = 0;
static int selected_row = 0;
static void keyboard_cb_edit_move_increment(lv_event_t * e)
{
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
const char * text = lv_textarea_get_text(ta);
float increment = atof(text);
if (increment < 0)
{
increment *= -1;
}
if (increment == 0 || increment > 999)
{
return; return;
}
unsigned short* items[] = {get_current_printer()->printer_config->printer_move_x_steps, get_current_printer()->printer_config->printer_move_y_steps, get_current_printer()->printer_config->printer_move_z_steps};
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);
}
char gcode[64]; static void edit_move_increment(int column, float* idx)
const char* extra = (amount > 0) ? "+" : ""; {
float* offsets[] = {(float*)x_offsets, (float*)y_offsets, (float*)z_offsets};
int row = idx - offsets[column];
bool absolute_coords = printer.absolute_coords; if (row < 3)
{
if (absolute_coords) { selected_row = 2 - row;
send_gcode(true, "G91"); }
else
{
selected_row = row - 3;
} }
const char * space = "%20"; selected_column = column;
lv_create_keyboard_text_entry(keyboard_cb_edit_move_increment, "Set increment", LV_KEYBOARD_MODE_NUMBER, LV_PCT(75), 6);
sprintf(gcode, "G1%s%s%s%.1f%sF6000", space, axis, extra, amount, space);
send_gcode(true, gcode);
if (absolute_coords) {
send_gcode(true, "G90");
}
} }
static void x_line_button_press(lv_event_t * e) { static void x_line_button_press(lv_event_t * e) {
float* data_pointer = (float*)lv_event_get_user_data(e); float* data_pointer = (float*)lv_event_get_user_data(e);
if (move_edit_mode)
{
edit_move_increment(0, data_pointer);
return;
}
float data = *data_pointer; float data = *data_pointer;
move_printer("X", data); current_printer_move_printer("X", data, true);
} }
static void y_line_button_press(lv_event_t * e) { static void y_line_button_press(lv_event_t * e) {
float* data_pointer = (float*)lv_event_get_user_data(e); float* data_pointer = (float*)lv_event_get_user_data(e);
if (move_edit_mode)
{
edit_move_increment(1, data_pointer);
return;
}
float data = *data_pointer; float data = *data_pointer;
move_printer("Y", data); current_printer_move_printer("Y", data, true);
} }
static void z_line_button_press(lv_event_t * e) { static void z_line_button_press(lv_event_t * e) {
float* data_pointer = (float*)lv_event_get_user_data(e); float* data_pointer = (float*)lv_event_get_user_data(e);
float data = *data_pointer;
move_printer("Z", data);
}
char x_pos_buff[12]; if (move_edit_mode)
{
edit_move_increment(2, data_pointer);
return;
}
float data = *data_pointer;
current_printer_move_printer("Z", data, true);
}
static void x_pos_update(lv_event_t * e){ static void x_pos_update(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
sprintf(x_pos_buff, "X: %.1f", printer.position[0]); char x_pos_buff[12];
sprintf(x_pos_buff, "X: %.1f", get_current_printer_data()->position[0]);
lv_label_set_text(label, x_pos_buff); lv_label_set_text(label, x_pos_buff);
} }
char y_pos_buff[12];
static void y_pos_update(lv_event_t * e){ static void y_pos_update(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
sprintf(y_pos_buff, "Y: %.1f", printer.position[1]); char y_pos_buff[12];
sprintf(y_pos_buff, "Y: %.1f", get_current_printer_data()->position[1]);
lv_label_set_text(label, y_pos_buff); lv_label_set_text(label, y_pos_buff);
} }
char z_pos_buff[12];
static void z_pos_update(lv_event_t * e){ static void z_pos_update(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
sprintf(z_pos_buff, "Z: %.2f", printer.position[2]); char z_pos_buff[12];
sprintf(z_pos_buff, "Z: %.2f", get_current_printer_data()->position[2]);
lv_label_set_text(label, z_pos_buff); lv_label_set_text(label, z_pos_buff);
} }
lv_event_cb_t button_callbacks[] = {x_line_button_press, y_line_button_press, z_line_button_press}; lv_event_cb_t button_callbacks[] = {x_line_button_press, y_line_button_press, z_line_button_press};
lv_event_cb_t position_callbacks[] = {x_pos_update, y_pos_update, z_pos_update}; lv_event_cb_t position_callbacks[] = {x_pos_update, y_pos_update, z_pos_update};
const float xy_offsets[] = {-100, -10, -1, 1, 10, 100};
const float z_offsets[] = {-25, -1, -0.1, 0.1, 1, 25};
const float* offsets[] = {
xy_offsets,
xy_offsets,
z_offsets
};
const char* xy_offset_labels[] = {"-100", "-10", "-1", "+1", "+10", "+100"};
const char* z_offset_labels[] = {"-25", "-1", "-0.1", "+0.1", "+1", "+25"};
const char** offset_labels[] = {
xy_offset_labels,
xy_offset_labels,
z_offset_labels
};
static void home_button_click(lv_event_t * e) { static void home_button_click(lv_event_t * e) {
if (printer.state == PRINTER_STATE_PRINTING) if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting)
return; return;
send_gcode(false, "G28"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureHome);
} }
static void disable_steppers_click(lv_event_t * e) { static void disable_steppers_click(lv_event_t * e) {
if (printer.state == PRINTER_STATE_PRINTING) if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting)
return; return;
send_gcode(true, "M18"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureDisableSteppers);
} }
static void stepper_state_update(lv_event_t * e){ static void switch_to_stat_panel(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * panel = lv_event_get_target(e);
lv_label_set_text(label, printer.homed_axis ? LV_SYMBOL_HOME " Steppers locked" : LV_SYMBOL_EYE_CLOSE " Steppers unlocked"); nav_buttons_setup(PANEL_STATS);
}
static void move_edit_toggle(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
move_edit_mode = lv_obj_get_state(btn) & LV_STATE_CHECKED;
}
static void line_custom_set(const char * axis, const char *text)
{
float pos = atof(text);
if (pos < 0 || pos > 500)
return;
current_printer_move_printer(axis, pos, false);
}
static void x_line_custom_callback(lv_event_t * e) {
const char * text = lv_textarea_get_text(lv_event_get_target(e));
line_custom_set("X", text);
}
static void y_line_custom_callback(lv_event_t * e) {
const char * text = lv_textarea_get_text(lv_event_get_target(e));
line_custom_set("Y", text);
}
static void z_line_custom_callback(lv_event_t * e) {
const char * text = lv_textarea_get_text(lv_event_get_target(e));
line_custom_set("Z", text);
}
static void x_line_custom(lv_event_t * e) {
lv_create_keyboard_text_entry(x_line_custom_callback, "Set X position", LV_KEYBOARD_MODE_NUMBER, LV_PCT(75), 6);
}
static void y_line_custom(lv_event_t * e) {
lv_create_keyboard_text_entry(y_line_custom_callback, "Set Y position", LV_KEYBOARD_MODE_NUMBER, LV_PCT(75), 6);
}
static void z_line_custom(lv_event_t * e) {
lv_create_keyboard_text_entry(z_line_custom_callback, "Set Z position", LV_KEYBOARD_MODE_NUMBER, LV_PCT(75), 6);
}
lv_event_cb_t custom_callbacks[] = {x_line_custom, y_line_custom, z_line_custom};
inline void root_panel_steppers_locked(lv_obj_t * root_panel){
const auto width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * panel = lv_create_empty_panel(root_panel);
lv_obj_set_size(panel, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, 0);
lv_layout_flex_column(panel, LV_FLEX_ALIGN_SPACE_BETWEEN, 0, 0);
lv_obj_t * home_button_row = lv_create_empty_panel(panel);
lv_obj_set_size(home_button_row, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(home_button_row);
lv_obj_t * btn = lv_btn_create(home_button_row);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, home_button_click, LV_EVENT_CLICKED, NULL);
lv_obj_set_flex_grow(btn, 1);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_HOME "Home");
lv_obj_center(label);
btn = lv_btn_create(home_button_row);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, disable_steppers_click, LV_EVENT_CLICKED, NULL);
lv_obj_set_flex_grow(btn, 1);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_EYE_CLOSE "Free");
lv_obj_center(label);
btn = lv_btn_create(home_button_row);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, switch_to_stat_panel, LV_EVENT_CLICKED, NULL);
lv_obj_set_flex_grow(btn, 1);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_SETTINGS "Param");
lv_obj_center(label);
btn = lv_btn_create(home_button_row);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, move_edit_toggle, LV_EVENT_CLICKED, NULL);
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
if (move_edit_mode)
{
lv_obj_add_state(btn, LV_STATE_CHECKED);
}
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_EDIT);
lv_obj_center(label);
float* offsets[] = {(float*)x_offsets, (float*)y_offsets, (float*)z_offsets};
char * labels[] = {(char*)x_offset_labels, (char*)y_offset_labels, (char*)z_offset_labels};
for (int row = 0; row < 3; row++) {
label = lv_label_btn_create(panel, custom_callbacks[row]);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label, "???");
lv_obj_set_width(label, width);
lv_obj_add_event_cb(label, position_callbacks[row], LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_t * row_panel = lv_create_empty_panel(panel);
lv_obj_set_size(row_panel, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(row_panel);
for (int col = 0; col < 6; col++)
{
btn = lv_btn_create(row_panel);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, button_callbacks[row], LV_EVENT_CLICKED, (void*)(offsets[row] + col));
lv_obj_set_flex_grow(btn, 1);
label = lv_label_create(btn);
lv_label_set_text(label, labels[row] + OFFSET_LABEL_SIZE * col);
lv_obj_center(label);
}
}
}
inline void root_panel_steppers_unlocked(lv_obj_t * root_panel){
lv_obj_t * panel = lv_create_empty_panel(root_panel);
lv_obj_set_size(panel, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_layout_flex_column(panel, LV_FLEX_ALIGN_CENTER);
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, LV_SYMBOL_EYE_CLOSE " Steppers unlocked");
lv_obj_t * btn_row = lv_create_empty_panel(panel);
lv_obj_set_size(btn_row, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(btn_row, LV_FLEX_ALIGN_CENTER);
lv_obj_t * btn = lv_btn_create(btn_row);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, home_button_click, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_HOME "Home Axis");
lv_obj_center(label);
btn = lv_btn_create(btn_row);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, switch_to_stat_panel, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_SETTINGS "Parameters");
lv_obj_center(label);
}
static void root_panel_state_update(lv_event_t * e){
if (last_homing_state == get_current_printer_data()->homed_axis)
return;
lv_obj_t * panel = lv_event_get_target(e);
last_homing_state = get_current_printer_data()->homed_axis;
lv_obj_clean(panel);
if (get_current_printer_data()->homed_axis)
root_panel_steppers_locked(panel);
else
root_panel_steppers_unlocked(panel);
} }
void move_panel_init(lv_obj_t* panel){ void move_panel_init(lv_obj_t* panel){
lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE); if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting){
const int button_size = 40; stats_panel_init(panel);
const int button_size_vertical = 40; return;
const int button_padding = 2;
const int x_offset = 15;
int y_pos = 75;
auto panel_width = TFT_HEIGHT - 40;
lv_obj_t * home_button = lv_btn_create(panel);
lv_obj_align(home_button, LV_ALIGN_TOP_LEFT, 10, 5);
lv_obj_add_event_cb(home_button, home_button_click, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(home_button, panel_width / 2 - 15, 30);
lv_obj_t * home_label = lv_label_create(home_button);
lv_label_set_text(home_label, LV_SYMBOL_HOME "Home Axis");
lv_obj_center(home_label);
lv_obj_t * disable_steppers_button = lv_btn_create(panel);
lv_obj_align(disable_steppers_button, LV_ALIGN_TOP_RIGHT, -10, 5);
lv_obj_add_event_cb(disable_steppers_button, disable_steppers_click, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(disable_steppers_button, panel_width / 2 - 15, 30);
lv_obj_t * disable_steppers_label = lv_label_create(disable_steppers_button);
lv_label_set_text(disable_steppers_label, LV_SYMBOL_EYE_CLOSE "Disable Step");
lv_obj_center(disable_steppers_label);
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, "???");
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 40);
lv_obj_add_event_cb(label, stepper_state_update, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
for (int i = 0; i < 3; i++) {
lv_obj_t * btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i]));
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][0]);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset + (button_size + button_padding) * 1, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i] + 1));
label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][1]);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset + (button_size + button_padding) * 2, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i] + 2));
label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][2]);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset + (button_size + button_padding) * 3, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i] + 3));
label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][3]);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset + (button_size + button_padding) * 4, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i] + 4));
label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][4]);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_set_size(btn, button_size, button_size_vertical);
lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x_offset + (button_size + button_padding) * 5, y_pos);
lv_obj_add_event_cb(btn, button_callbacks[i], LV_EVENT_CLICKED, (void*)(offsets[i] + 5));
label = lv_label_create(btn);
lv_label_set_text(label, offset_labels[i][5]);
lv_obj_center(label);
label = lv_label_create(panel);
lv_label_set_text(label, "???");
lv_obj_align(label, LV_ALIGN_TOP_LEFT, x_offset, y_pos - 15);\
lv_obj_add_event_cb(label, position_callbacks[i], LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
y_pos += 60;
} }
lv_msg_send(DATA_PRINTER_DATA, &printer); calculate_offsets_from_current_printer();
last_homing_state = !get_current_printer_data()->homed_axis;
lv_obj_add_event_cb(panel, root_panel_state_update, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, panel, NULL);
} }

View File

@@ -4,6 +4,13 @@
void settings_panel_init(lv_obj_t* panel); void settings_panel_init(lv_obj_t* panel);
void temp_panel_init(lv_obj_t* panel); void temp_panel_init(lv_obj_t* panel);
void print_panel_init(lv_obj_t* panel); void files_panel_init(lv_obj_t* panel);
void move_panel_init(lv_obj_t* panel); void move_panel_init(lv_obj_t* panel);
void progress_panel_init(lv_obj_t* panel); void progress_panel_init(lv_obj_t* panel);
void macros_panel_init(lv_obj_t* panel);
void stats_panel_init(lv_obj_t* panel);
void printer_panel_init(lv_obj_t* panel);
void error_panel_init(lv_obj_t* panel);
void connecting_panel_init(lv_obj_t* panel);
void settings_section_device(lv_obj_t* panel);

View File

@@ -1,88 +0,0 @@
#include "lvgl.h"
#include "panel.h"
#include "../../core/data_setup.h"
#include "../../core/files_query.h"
#include "../../conf/global_config.h"
#include <HardwareSerial.h>
#include <HTTPClient.h>
FILESYSTEM_FILE* selected_file = NULL;
static void btn_print_file(lv_event_t * e){
lv_obj_t * panel = (lv_obj_t*)lv_event_get_user_data(e);
lv_obj_del(panel);
char* buff = (char*)malloc(128 + strlen(selected_file->name));
sprintf(buff, "http://%s:%d/printer/print/start?filename=%s", global_config.klipperHost, global_config.klipperPort, selected_file->name);
HTTPClient client;
client.begin(buff);
int httpCode = client.POST("");
Serial.printf("Print start: HTTP %d\n", httpCode);
}
static void btn_print_back(lv_event_t * e){
lv_obj_t * panel = (lv_obj_t*)lv_event_get_user_data(e);
lv_obj_del(panel);
}
static void btn_print_file_verify(lv_event_t * e){
lv_obj_t * btn = lv_event_get_target(e);
selected_file = (FILESYSTEM_FILE*)lv_event_get_user_data(e);
lv_obj_t * panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, TFT_HEIGHT - 40, TFT_WIDTH - 30);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, "Print File");
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 0, 0);
label = lv_label_create(panel);
lv_label_set_text(label, selected_file->name);
lv_obj_align(label, LV_ALIGN_CENTER, 0, -20);
lv_obj_set_width(label, TFT_HEIGHT - 90);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 10, -10);
lv_obj_set_size(btn, 40, 40);
lv_obj_add_event_cb(btn, btn_print_back, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
lv_obj_center(label);
btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -10, -10);
lv_obj_set_size(btn, 40, 40);
lv_obj_add_event_cb(btn, btn_print_file, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_OK);
lv_obj_center(label);
}
void print_panel_init(lv_obj_t* panel){
if (printer.state == PRINTER_STATE_PRINTING || printer.state == PRINTER_STATE_PAUSED){
progress_panel_init(panel);
return;
}
auto panel_width = TFT_HEIGHT - 40;
auto panel_height_margin = TFT_WIDTH - 10;
auto panel_width_margin = panel_width - 10;
lv_obj_t * list = lv_list_create(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();
int count = 0;
while (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++;
}
}

View File

@@ -0,0 +1,299 @@
#include "panel.h"
#include "../../conf/global_config.h"
#include "../../core/data_setup.h"
#include "../ui_utils.h"
#include "../../core/lv_setup.h"
#include <stdio.h>
#include "../nav_buttons.h"
#include "../macros.h"
const char * printer_status[] = {
"Offline",
"Error",
"Idle",
"Printing",
"Paused"
};
const static lv_point_t line_points[] = { {0, 0}, {(short int)((CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2) * 0.85f), 0} };
static void update_printer_name_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
BasePrinter* printer = get_printer(config_index);
lv_label_set_text(label, printer->printer_config->printer_name[0] == 0 ? printer->printer_config->printer_host : printer->printer_config->printer_name);
}
static void update_printer_status_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if (config_index == get_current_printer_index())
{
lv_label_set_text(label, "In Control");
return;
}
lv_label_set_text(label, printer_status[printer->state]);
}
static void update_printer_label_visible_active_printer(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
if (config_index == get_current_printer_index())
{
lv_label_set_text(label, LV_SYMBOL_WIFI);
}
else
{
lv_label_set_text(label, "");
}
}
static void update_printer_percentage_bar(lv_event_t * e)
{
lv_obj_t * percentage = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if (printer->state == PrinterState::PrinterStatePrinting || printer->state == PrinterState::PrinterStatePaused)
{
lv_bar_set_value(percentage, printer->print_progress * 100, LV_ANIM_OFF);
}
else
{
lv_bar_set_value(percentage, 0, LV_ANIM_OFF);
}
}
static void update_printer_percentage_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if (printer->state == PrinterState::PrinterStatePrinting || printer->state == PrinterState::PrinterStatePaused)
{
char percentage_buffer[12];
sprintf(percentage_buffer, "%.2f%%", printer->print_progress * 100);
lv_label_set_text(label, percentage_buffer);
}
else
{
lv_label_set_text(label, "-%");
}
}
static void update_printer_control_button_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if (printer->power_devices > 0 && (config_index == get_current_printer_index() || printer->state == PrinterState::PrinterStateOffline))
{
lv_label_set_text(label, "Power");
}
else
{
lv_label_set_text(label, "Control");
}
}
static void btn_set_secondary_button_text(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if (config_index == get_current_printer_index())
{
lv_label_set_text(label, LV_SYMBOL_SETTINGS);
}
else
{
lv_label_set_text(label, LV_SYMBOL_TRASH);
}
}
static void btn_enable_control(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
if ((config_index == get_current_printer_index() || printer->state == PrinterState::PrinterStateOffline) && printer->power_devices <= 0)
{
// Disable
lv_obj_add_state(btn, LV_STATE_DISABLED);
}
else
{
// Enable
lv_obj_clear_state(btn, LV_STATE_DISABLED);
}
}
PrinterConfiguration * keyboard_config = NULL;
static void keyboard_callback(lv_event_t * e){
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
const char * text = lv_textarea_get_text(ta);
strcpy(keyboard_config->printer_name, text);
write_global_config();
lv_msg_send(DATA_PRINTER_MINIMAL, NULL);
}
static void btn_printer_secondary(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
BasePrinter* printer = get_printer(config_index);
if (config_index == get_current_printer_index())
{
nav_buttons_setup(PANEL_SETTINGS);
return;
}
global_config_delete_printer(config_index);
}
static void btn_printer_rename(lv_event_t * e)
{
int config_index = (int)lv_event_get_user_data(e);
BasePrinter* printer = get_printer(config_index);
keyboard_config = printer->printer_config;
lv_create_keyboard_text_entry(keyboard_callback, "Rename Printer", LV_KEYBOARD_MODE_TEXT_LOWER, CYD_SCREEN_WIDTH_PX * 0.75, 24, keyboard_config->printer_name, false);
}
static void btn_printer_activate(lv_event_t * e)
{
lv_obj_t * label = lv_event_get_target(e);
int config_index = (int)lv_event_get_user_data(e);
PrinterDataMinimal* printer = get_printer_data_minimal(config_index);
BasePrinter* printer_full = get_printer(config_index);
if (printer->power_devices > 0 && (config_index == get_current_printer_index() || printer->state == PrinterState::PrinterStateOffline))
{
macros_draw_power_fullscreen(printer_full);
return;
}
set_current_printer(config_index);
lv_msg_send(DATA_PRINTER_MINIMAL, NULL);
}
static void btn_printer_add(lv_event_t * e)
{
global_config_add_new_printer();
}
void create_printer_ui(int index, lv_obj_t * root)
{
auto width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * data_row_name = lv_create_empty_panel(root);
lv_layout_flex_row(data_row_name, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_obj_set_size(data_row_name, width, LV_SIZE_CONTENT);
lv_obj_t * label = lv_label_create(data_row_name);
lv_obj_add_event_cb(label, update_printer_name_text, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
label = lv_label_create(data_row_name);
lv_obj_add_event_cb(label, update_printer_label_visible_active_printer, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
label = lv_label_create(data_row_name);
lv_obj_add_event_cb(label, update_printer_status_text, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
lv_obj_t * progress_row = lv_create_empty_panel(root);
lv_layout_flex_row(progress_row);
lv_obj_set_size(progress_row, width, LV_SIZE_CONTENT);
lv_obj_t * progress_bar = lv_bar_create(progress_row);
lv_obj_set_flex_grow(progress_bar, 1);
lv_obj_add_event_cb(progress_bar, update_printer_percentage_bar, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, progress_bar, (void*)index);
label = lv_label_create(progress_row);
lv_obj_set_style_text_font(label, &CYD_SCREEN_FONT_SMALL, 0);
lv_obj_add_event_cb(label, update_printer_percentage_text, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
lv_obj_t * button_row = lv_create_empty_panel(root);
lv_layout_flex_row(button_row);
lv_obj_set_size(button_row, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t * btn = lv_btn_create(button_row);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_printer_secondary, LV_EVENT_CLICKED, (void*)index);
label = lv_label_create(btn);
lv_obj_center(label);
lv_obj_add_event_cb(label, btn_set_secondary_button_text, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
btn = lv_btn_create(button_row);
lv_obj_set_flex_grow(btn, 2);
lv_obj_add_event_cb(btn, btn_printer_rename, LV_EVENT_CLICKED, (void*)index);
label = lv_label_create(btn);
lv_label_set_text(label, "Rename");
lv_obj_center(label);
btn = lv_btn_create(button_row);
lv_obj_set_flex_grow(btn, 2);
lv_obj_add_event_cb(btn, btn_printer_activate, LV_EVENT_CLICKED, (void*)index);
lv_obj_add_event_cb(btn, btn_enable_control, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, btn, (void*)index);
label = lv_label_create(btn);
lv_obj_center(label);
lv_obj_add_event_cb(label, update_printer_control_button_text, LV_EVENT_MSG_RECEIVED, (void*)index);
lv_msg_subsribe_obj(DATA_PRINTER_MINIMAL, label, (void*)index);
lv_obj_t * line = lv_line_create(root);
lv_line_set_points(line, line_points, 2);
lv_obj_set_style_line_width(line, 1, 0);
lv_obj_set_style_line_color(line, lv_color_hex(0xAAAAAA), 0);
}
void printer_panel_init(lv_obj_t* panel)
{
lv_obj_t * inner_panel = lv_create_empty_panel(panel);
lv_obj_align(inner_panel, LV_ALIGN_TOP_LEFT, CYD_SCREEN_GAP_PX, 0);
lv_obj_set_size(inner_panel, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_layout_flex_column(inner_panel);
lv_obj_set_scrollbar_mode(inner_panel, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(lv_create_empty_panel(inner_panel), 0, 0);
for (int i = 0; i < get_printer_count(); i++){
create_printer_ui(i, inner_panel);
}
// Add Printer Button
if (get_printer_count() < PRINTER_CONFIG_COUNT){
lv_obj_t * btn = lv_btn_create(inner_panel);
lv_obj_set_size(btn, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, btn_printer_add, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Add Printer");
lv_obj_center(label);
}
lv_obj_set_size(lv_create_empty_panel(inner_panel), 0, 0);
lv_msg_send(DATA_PRINTER_MINIMAL, NULL);
}

View File

@@ -1,6 +1,8 @@
#include "panel.h" #include "panel.h"
#include "../../core/data_setup.h"
#include <stdio.h> #include <stdio.h>
#include "../ui_utils.h"
#include "../../core/printer_integration.hpp"
#include "../../core/current_printer.h"
char time_buffer[12]; char time_buffer[12];
@@ -14,81 +16,138 @@ char* time_display(unsigned long time){
static void progress_bar_update(lv_event_t* e){ static void progress_bar_update(lv_event_t* e){
lv_obj_t * bar = lv_event_get_target(e); lv_obj_t * bar = lv_event_get_target(e);
lv_bar_set_value(bar, printer.print_progress * 100, LV_ANIM_ON); lv_bar_set_value(bar, get_current_printer_data()->print_progress * 100, LV_ANIM_ON);
} }
static void update_printer_data_elapsed_time(lv_event_t * e){ static void update_printer_data_elapsed_time(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, time_display(printer.elapsed_time_s)); lv_label_set_text(label, time_display(get_current_printer_data()->elapsed_time_s));
} }
static void update_printer_data_remaining_time(lv_event_t * e){ static void update_printer_data_remaining_time(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, time_display(printer.remaining_time_s)); lv_label_set_text(label, time_display(get_current_printer_data()->remaining_time_s));
}
static void update_printer_data_stats(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char buff[256] = {0};
switch (get_current_printer()->printer_config->show_stats_on_progress_panel)
{
case SHOW_STATS_ON_PROGRESS_PANEL_LAYER:
sprintf(buff, "Layer %d of %d", get_current_printer_data()->current_layer, get_current_printer_data()->total_layers);
break;
case SHOW_STATS_ON_PROGRESS_PANEL_PARTIAL:
sprintf(buff, "Position: X%.2f Y%.2f\nFeedrate: %d mm/s\nFilament Used: %.2f m\nLayer %d of %d",
get_current_printer_data()->position[0], get_current_printer_data()->position[1], get_current_printer_data()->feedrate_mm_per_s, get_current_printer_data()->filament_used_mm / 1000, get_current_printer_data()->current_layer, get_current_printer_data()->total_layers);
break;
case SHOW_STATS_ON_PROGRESS_PANEL_ALL:
sprintf(buff, "Pressure Advance: %.3f (%.2fs)\nPosition: X%.2f Y%.2f Z%.2f\nFeedrate: %d mm/s\nFilament Used: %.2f m\nFan: %.0f%%\nSpeed: %.0f%%\nFlow: %.0f%%\nLayer %d of %d",
get_current_printer_data()->pressure_advance, get_current_printer_data()->smooth_time, get_current_printer_data()->position[0], get_current_printer_data()->position[1], get_current_printer_data()->position[2], get_current_printer_data()->feedrate_mm_per_s, get_current_printer_data()->filament_used_mm / 1000, get_current_printer_data()->fan_speed * 100, get_current_printer_data()->speed_mult * 100, get_current_printer_data()->extrude_mult * 100, get_current_printer_data()->current_layer, get_current_printer_data()->total_layers);
break;
}
lv_label_set_text(label, buff);
} }
static void update_printer_data_percentage(lv_event_t * e){ static void update_printer_data_percentage(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
char percentage_buffer[12]; char percentage_buffer[12];
sprintf(percentage_buffer, "%.2f%%", printer.print_progress * 100); sprintf(percentage_buffer, "%.2f%%", get_current_printer_data()->print_progress * 100);
lv_label_set_text(label, percentage_buffer); lv_label_set_text(label, percentage_buffer);
} }
static void btn_click_stop(lv_event_t * e){ static void btn_click_stop(lv_event_t * e){
send_gcode(true, "CANCEL_PRINT"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureStop);
} }
static void btn_click_pause(lv_event_t * e){ static void btn_click_pause(lv_event_t * e){
send_gcode(true, "PAUSE"); current_printer_execute_feature(PrinterFeatures::PrinterFeaturePause);
} }
static void btn_click_resume(lv_event_t * e){ static void btn_click_resume(lv_event_t * e){
send_gcode(true, "RESUME"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureResume);
}
static void btn_click_estop(lv_event_t * e){
current_printer_execute_feature(PrinterFeatures::PrinterFeatureEmergencyStop);
} }
void progress_panel_init(lv_obj_t* panel){ void progress_panel_init(lv_obj_t* panel){
auto panel_width = TFT_HEIGHT - 40; auto panel_width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 3;
auto panel_width_margin = panel_width - 30; const auto button_size_mult = 1.3f;
// Emergency Stop
if (global_config.show_estop && (get_current_printer()->supports_feature(PrinterFeatureEmergencyStop))){
lv_obj_t * btn = lv_btn_create(panel);
lv_obj_add_event_cb(btn, btn_click_estop, LV_EVENT_CLICKED, NULL);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, CYD_SCREEN_GAP_PX);
lv_obj_set_style_bg_color(btn, lv_color_hex(0xFF0000), LV_PART_MAIN);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_POWER " EMERGENCY STOP");
lv_obj_center(label);
}
lv_obj_t * center_panel = lv_create_empty_panel(panel);
lv_obj_set_size(center_panel, panel_width, LV_SIZE_CONTENT);
lv_layout_flex_column(center_panel);
// Only align progress bar to top mid if necessary to make room for all extras
if (get_current_printer()->printer_config->show_stats_on_progress_panel == SHOW_STATS_ON_PROGRESS_PANEL_ALL && CYD_SCREEN_HEIGHT_PX <= 320)
{
lv_obj_align(center_panel, LV_ALIGN_TOP_MID, 0, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX+(3 * CYD_SCREEN_GAP_PX));
}
else
{
lv_obj_align(center_panel, LV_ALIGN_CENTER, 0, 0);
}
// Filename // Filename
lv_obj_t * label = lv_label_create(panel); lv_obj_t * label = lv_label_create(center_panel);
lv_label_set_text(label, printer.print_filename); lv_label_set_text(label, get_current_printer_data()->print_filename);
lv_obj_align(label, LV_ALIGN_CENTER, 0, -40); if (global_config.full_filenames) lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); else lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(label, panel_width_margin); lv_obj_set_width(label, panel_width);
// Progress Bar // Progress Bar
lv_obj_t * bar = lv_bar_create(panel); lv_obj_t * bar = lv_bar_create(center_panel);
lv_obj_align(bar, LV_ALIGN_CENTER, 0, 0); lv_obj_set_size(bar, panel_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX * 0.75f);
lv_obj_set_size(bar, panel_width_margin, 20);
lv_obj_add_event_cb(bar, progress_bar_update, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(bar, progress_bar_update, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, bar, NULL); lv_msg_subsribe_obj(DATA_PRINTER_DATA, bar, NULL);
// Time
lv_obj_t * time_est_panel = lv_create_empty_panel(center_panel);
lv_obj_set_size(time_est_panel, panel_width, LV_SIZE_CONTENT);
// Elapsed Time // Elapsed Time
label = lv_label_create(panel); label = lv_label_create(time_est_panel);
lv_label_set_text(label, "???"); lv_label_set_text(label, "???");
lv_obj_align(label, LV_ALIGN_LEFT_MID, 10, 20); lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_add_event_cb(label, update_printer_data_elapsed_time, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(label, update_printer_data_elapsed_time, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL); lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
// Remaining Time // Remaining Time
label = lv_label_create(panel); label = lv_label_create(time_est_panel);
lv_label_set_text(label, "???"); lv_label_set_text(label, "???");
lv_obj_align(label, LV_ALIGN_RIGHT_MID, -10, 20); lv_obj_align(label, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(label, update_printer_data_remaining_time, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(label, update_printer_data_remaining_time, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL); lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
// Percentage // Percentage
label = lv_label_create(panel); label = lv_label_create(time_est_panel);
lv_label_set_text(label, "???"); lv_label_set_text(label, "???");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 20); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(label, update_printer_data_percentage, LV_EVENT_MSG_RECEIVED, NULL); lv_obj_add_event_cb(label, update_printer_data_percentage, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL); lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
// Stop Button // Stop Button
lv_obj_t * btn = lv_btn_create(panel); lv_obj_t * btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -10, -10); lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -1 * CYD_SCREEN_GAP_PX, -1 * CYD_SCREEN_GAP_PX);
lv_obj_set_size(btn, 40, 40); lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX * button_size_mult, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX * button_size_mult);
lv_obj_add_event_cb(btn, btn_click_stop, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_click_stop, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn); label = lv_label_create(btn);
@@ -96,10 +155,8 @@ void progress_panel_init(lv_obj_t* panel){
lv_obj_center(label); lv_obj_center(label);
// Resume Button // Resume Button
if (printer.state == PRINTER_STATE_PAUSED){ if (get_current_printer_data()->state == PrinterState::PrinterStatePaused){
btn = lv_btn_create(panel); btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -60, -10);
lv_obj_set_size(btn, 40, 40);
lv_obj_add_event_cb(btn, btn_click_resume, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_click_resume, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn); label = lv_label_create(btn);
@@ -109,8 +166,6 @@ void progress_panel_init(lv_obj_t* panel){
// Pause Button // Pause Button
else { else {
btn = lv_btn_create(panel); btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -60, -10);
lv_obj_set_size(btn, 40, 40);
lv_obj_add_event_cb(btn, btn_click_pause, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_click_pause, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn); label = lv_label_create(btn);
@@ -118,5 +173,15 @@ void progress_panel_init(lv_obj_t* panel){
lv_obj_center(label); lv_obj_center(label);
} }
lv_msg_send(DATA_PRINTER_DATA, &printer); lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -2 * CYD_SCREEN_GAP_PX - CYD_SCREEN_MIN_BUTTON_WIDTH_PX * button_size_mult, -1 * CYD_SCREEN_GAP_PX);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX * button_size_mult, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX * button_size_mult);
if (get_current_printer()->printer_config->show_stats_on_progress_panel > SHOW_STATS_ON_PROGRESS_PANEL_NONE)
{
label = lv_label_create(panel);
lv_obj_align(label, LV_ALIGN_BOTTOM_LEFT, CYD_SCREEN_GAP_PX, -1 * CYD_SCREEN_GAP_PX);
lv_obj_set_style_text_font(label, &CYD_SCREEN_FONT_SMALL, 0);
lv_obj_add_event_cb(label, update_printer_data_stats, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
}
} }

View File

@@ -3,53 +3,86 @@
#include "../../core/screen_driver.h" #include "../../core/screen_driver.h"
#include "../../conf/global_config.h" #include "../../conf/global_config.h"
#include "../main_ui.h" #include "../main_ui.h"
#include "../ui_utils.h"
#include <Esp.h>
#include "../../core/lv_setup.h"
#include "../ota_setup.h"
#include "../nav_buttons.h"
#include "../../core/printer_integration.hpp"
#ifndef REPO_VERSION
#define REPO_VERSION "Unknown"
#endif // REPO_VERSION
static void invert_color_switch(lv_event_t * e){ static void invert_color_switch(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);
global_config.invertColors = checked; global_config.printer_config[global_config.printer_index].invert_colors = checked;
WriteGlobalConfig(); write_global_config();
set_invert_display(); set_invert_display();
} }
static void reset_calibration_click(lv_event_t * e){ static void reset_calibration_click(lv_event_t * e){
global_config.screenCalibrated = false; global_config.screen_calibrated = false;
WriteGlobalConfig(); write_global_config();
ESP.restart();
}
static void reset_click(lv_event_t * e){
ESP.restart(); ESP.restart();
} }
static void reset_wifi_click(lv_event_t * e){ static void reset_wifi_click(lv_event_t * e){
global_config.wifiConfigured = false; global_config.wifi_configured = false;
global_config.ipConfigured = false; global_config.wifi_configuration_skipped = false;
WriteGlobalConfig(); write_global_config();
ESP.restart();
}
static void reset_ip_click(lv_event_t * e){
get_current_printer()->printer_config->setup_complete = false;
write_global_config();
ESP.restart(); ESP.restart();
} }
static void light_mode_switch(lv_event_t * e){ static void light_mode_switch(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);
global_config.lightMode = checked; get_current_printer()->printer_config->light_mode = checked;
WriteGlobalConfig(); write_global_config();
set_color_scheme(); set_color_scheme();
} }
static void filament_move_mode_switch(lv_event_t * e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
get_current_printer()->printer_config->custom_filament_move_macros = checked;
write_global_config();
}
static void show_stats_on_progress_panel_dropdown(lv_event_t * e){
auto selected = lv_dropdown_get_selected(lv_event_get_target(e));
get_current_printer()->printer_config->show_stats_on_progress_panel = selected;
write_global_config();
}
static void theme_dropdown(lv_event_t * e){ static void theme_dropdown(lv_event_t * e){
lv_obj_t * dropdown = lv_event_get_target(e); lv_obj_t * dropdown = lv_event_get_target(e);
auto selected = lv_dropdown_get_selected(dropdown); auto selected = lv_dropdown_get_selected(dropdown);
global_config.color_scheme = selected; get_current_printer()->printer_config->color_scheme = selected;
set_color_scheme(); set_color_scheme();
WriteGlobalConfig(); write_global_config();
} }
const char* brightness_options = "100%\n75%\n50%\n25%"; const char* brightness_options = "100%\n75%\n50%\n25%";
const char brightness_options_values[] = { 255, 192, 128, 64 }; const unsigned char brightness_options_values[] = { 255, 192, 128, 64 };
static void brightness_dropdown(lv_event_t * e){ static void brightness_dropdown(lv_event_t * e){
lv_obj_t * dropdown = lv_event_get_target(e); lv_obj_t * dropdown = lv_event_get_target(e);
auto selected = lv_dropdown_get_selected(dropdown); auto selected = lv_dropdown_get_selected(dropdown);
global_config.brightness = brightness_options_values[selected]; global_config.brightness = brightness_options_values[selected];
set_screen_brightness(); set_screen_brightness();
WriteGlobalConfig(); write_global_config();
} }
const char* wake_timeout_options = "1m\n2m\n5m\n10m\n15m\n30m\n1h\n2h\n4h"; const char* wake_timeout_options = "1m\n2m\n5m\n10m\n15m\n30m\n1h\n2h\n4h";
@@ -58,143 +91,231 @@ const char wake_timeout_options_values[] = { 1, 2, 5, 10, 15, 30, 60, 120, 240
static void wake_timeout_dropdown(lv_event_t * e){ static void wake_timeout_dropdown(lv_event_t * e){
lv_obj_t * dropdown = lv_event_get_target(e); lv_obj_t * dropdown = lv_event_get_target(e);
auto selected = lv_dropdown_get_selected(dropdown); auto selected = lv_dropdown_get_selected(dropdown);
global_config.screenTimeout = wake_timeout_options_values[selected]; global_config.screen_timeout = wake_timeout_options_values[selected];
set_screen_timer_period(); set_screen_timer_period();
WriteGlobalConfig(); write_global_config();
}
static void dualusb_screen_fix_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.display_mode = checked;
write_global_config();
ESP.restart();
}
static void disable_m117_messaging_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.disable_m117_messaging = checked;
write_global_config();
}
static void sort_macros_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.sort_macros = checked;
write_global_config();
}
static void show_estop_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.show_estop = checked;
write_global_config();
}
static void full_filenames_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.full_filenames = checked;
write_global_config();
}
static void double_size_gcode_img_switch(lv_event_t* e){
auto state = lv_obj_get_state(lv_event_get_target(e));
bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
global_config.double_size_gcode_img = checked;
write_global_config();
} }
static void rotate_screen_switch(lv_event_t* e){ static void rotate_screen_switch(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);
global_config.rotateScreen = checked; global_config.rotate_screen = checked;
global_config.screenCalibrated = false; global_config.screen_calibrated = false;
WriteGlobalConfig(); write_global_config();
ESP.restart(); ESP.restart();
} }
static void on_during_print_switch(lv_event_t* e){ static void on_during_print_switch(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);
global_config.onDuringPrint = checked; global_config.on_during_print = checked;
check_if_screen_needs_to_be_disabled(); check_if_screen_needs_to_be_disabled();
WriteGlobalConfig(); write_global_config();
} }
int y_offset = 0; static void btn_ota_do_update(lv_event_t * e){
const int y_element_size = 50; set_ready_for_ota_update();
const int y_seperator_size = 1; }
const int y_seperator_x_padding = 50;
const int panel_width = TFT_HEIGHT - 40;
const int y_element_x_padding = 30;
const static lv_point_t line_points[] = { {0, 0}, {panel_width - y_seperator_x_padding, 0} };
void create_settings_widget(const char* label_text, lv_obj_t* object, lv_obj_t* root_panel){ static void auto_ota_update_switch(lv_event_t* e){
lv_obj_t * panel = lv_obj_create(root_panel); auto state = lv_obj_get_state(lv_event_get_target(e));
lv_obj_set_style_border_width(panel, 0, 0); bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
lv_obj_set_style_bg_opa(panel, LV_OPA_TRANSP, 0); global_config.auto_ota_update = checked;
lv_obj_set_style_pad_all(panel, 0, 0); write_global_config();
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, y_offset); }
lv_obj_set_size(panel, panel_width - y_element_x_padding, y_element_size);
lv_obj_t * line = lv_line_create(panel); static void multi_printer_switch(lv_event_t* e){
lv_line_set_points(line, line_points, 2); auto state = lv_obj_get_state(lv_event_get_target(e));
lv_obj_set_style_line_width(line, y_seperator_size, 0); bool checked = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
lv_obj_set_style_line_color(line, lv_color_hex(0xAAAAAA), 0); global_config.multi_printer_mode = checked;
lv_obj_align(line, LV_ALIGN_BOTTOM_MID, 0, 0); write_global_config();
nav_buttons_setup(PANEL_SETTINGS);
}
const char* estimated_time_options = "Percentage\nInterpolated\nSlicer";
static void estimated_time_dropdown(lv_event_t * e){
lv_obj_t * dropdown = lv_event_get_target(e);
get_current_printer()->printer_config->remaining_time_calc_mode = lv_dropdown_get_selected(dropdown);
write_global_config();
}
#define PRINTER_SPECIFIC_SETTING global_config.multi_printer_mode ? "Stored per printer" : NULL
void settings_section_theming(lv_obj_t* panel)
{
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, "Theming");
lv_create_custom_menu_dropdown("Theme", panel, theme_dropdown, "Blue\nGreen\nLime\nGrey\nYellow\nOrange\nRed\nPurple", get_current_printer()->printer_config->color_scheme, NULL, PRINTER_SPECIFIC_SETTING);
lv_create_custom_menu_switch("Light Mode", panel, light_mode_switch, get_current_printer()->printer_config->light_mode, NULL, PRINTER_SPECIFIC_SETTING);
}
void settings_section_behaviour(lv_obj_t* panel)
{
PrinterType printer_type = get_current_printer()->printer_config->printer_type;
bool is_klipper = printer_type == PrinterTypeKlipper || printer_type == PrinterTypeKlipperSerial;
bool is_octo = printer_type == PrinterTypeOctoprint;
bool is_bambu = printer_type == PrinterTypeBambuLocal;
lv_obj_t * label = lv_label_create(panel); lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, label_text); lv_label_set_text(label, "\nBehaviour");
lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
if (is_klipper)
{
lv_create_custom_menu_dropdown("Estimated Time", panel, estimated_time_dropdown, estimated_time_options, get_current_printer()->printer_config->remaining_time_calc_mode, NULL, PRINTER_SPECIFIC_SETTING);
lv_create_custom_menu_dropdown("Stats in Progress Screen", panel, show_stats_on_progress_panel_dropdown, "None\nLayers\nPartial\nAll", get_current_printer()->printer_config->show_stats_on_progress_panel, NULL, PRINTER_SPECIFIC_SETTING);
}
else if (is_bambu)
{
lv_create_custom_menu_dropdown("Stats in Progress Screen", panel, show_stats_on_progress_panel_dropdown, "None\nLayers", get_current_printer()->printer_config->show_stats_on_progress_panel, NULL, PRINTER_SPECIFIC_SETTING);
}
lv_obj_set_parent(object, panel); #ifndef CYD_SCREEN_DISABLE_TIMEOUT
lv_obj_align(object, LV_ALIGN_RIGHT_MID, 0, 0); int wake_timeout_settings_index = 0;
y_offset += y_element_size; for (int i = 0; i < SIZEOF(wake_timeout_options_values); i++){
if (wake_timeout_options_values[i] == global_config.screen_timeout){
wake_timeout_settings_index = i;
break;
}
}
lv_create_custom_menu_dropdown("Wake Timeout", panel, wake_timeout_dropdown, wake_timeout_options, wake_timeout_settings_index);
#endif
#ifndef CYD_SCREEN_DISABLE_TIMEOUT
lv_create_custom_menu_switch("Screen On During Print", panel, on_during_print_switch, global_config.on_during_print);
#endif
lv_create_custom_menu_switch("Multi Printer Mode", panel, multi_printer_switch, global_config.multi_printer_mode);
if (is_klipper)
{
lv_create_custom_menu_switch("Disable M117 Messaging", panel, disable_m117_messaging_switch, global_config.disable_m117_messaging);
lv_create_custom_menu_switch("Custom Filament Move Macros", panel, filament_move_mode_switch, get_current_printer()->printer_config->custom_filament_move_macros, NULL,
global_config.multi_printer_mode
? "Calls FILAMENT_RETRACT and\nFILAMENT_EXTRUDE in temperature menu\nwhen enabled. Stored per printer."
: "Calls FILAMENT_RETRACT and\nFILAMENT_EXTRUDE in temperature menu\nwhen enabled");
lv_create_custom_menu_switch("Show Emergency Stop", panel, show_estop_switch, global_config.show_estop);
}
lv_create_custom_menu_switch("Sort Macros A->Z", panel, sort_macros_switch, global_config.sort_macros);
lv_create_custom_menu_switch("Show Full Filenames", panel, full_filenames_switch, global_config.full_filenames);
lv_create_custom_menu_switch("2X Size Gcode Image", panel, double_size_gcode_img_switch, global_config.double_size_gcode_img);
lv_create_custom_menu_button("Configure Printer Host", panel, reset_ip_click, "Restart");
}
void settings_section_device(lv_obj_t* panel)
{
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, "\nDevice");
int brightness_settings_index = 0;
for (int i = 0; i < SIZEOF(brightness_options_values); i++){
if (brightness_options_values[i] == global_config.brightness){
brightness_settings_index = i;
break;
}
}
lv_create_custom_menu_dropdown("Brightness", panel, brightness_dropdown, brightness_options, brightness_settings_index);
#ifndef CYD_SCREEN_DISABLE_INVERT_COLORS
lv_create_custom_menu_switch("Invert Colors", panel, invert_color_switch, global_config.printer_config[global_config.printer_index].invert_colors, NULL, (global_config.multi_printer_mode) ? "Stored per printer"
#ifdef CYD_SCREEN_DRIVER_ESP32_2432S028R
"\nIntended for the 2.8\" dual USB model screen" : "Intended for the 2.8\" dual USB model screen"
#else
: NULL
#endif
);
#endif // CYD_SCREEN_DISABLE_INVERT_COLORS
#ifdef CYD_SCREEN_DRIVER_ESP32_2432S028R
lv_create_custom_menu_switch("Screen Color Fix", panel, dualusb_screen_fix_switch, global_config.display_mode, NULL, "ONLY for the 2.8\" dual USB model screen");
#endif
#if defined(CYD_SCREEN_DRIVER_ESP32_SMARTDISPLAY) && !defined(CYD_SCREEN_DISABLE_TOUCH_CALIBRATION)
// TODO: Rotating screen requires different calibration points.
#else
lv_create_custom_menu_switch("Rotate Screen", panel, rotate_screen_switch, global_config.rotate_screen);
#endif
lv_create_custom_menu_switch("Auto Update", panel, auto_ota_update_switch, global_config.auto_ota_update);
lv_create_custom_menu_label("Version", panel, REPO_VERSION " ");
if (ota_has_update()){
lv_obj_t *btn = lv_btn_create(panel);
lv_obj_add_event_cb(btn, btn_ota_do_update, LV_EVENT_CLICKED, NULL);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Update to %s", ota_new_version_name().c_str());
lv_obj_center(label);
lv_create_custom_menu_entry("Device", btn, panel);
}
else {
lv_create_custom_menu_label("Device", panel, ARDUINO_BOARD " ");
}
#ifndef CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
lv_create_custom_menu_button("Calibrate Touch", panel, reset_calibration_click, "Restart");
#endif // CYD_SCREEN_DISABLE_TOUCH_CALIBRATION
lv_create_custom_menu_button("Configure WiFi", panel, reset_wifi_click, "Restart");
lv_create_custom_menu_button("Restart ESP", panel, reset_click, "Restart");
} }
void settings_panel_init(lv_obj_t* panel){ void settings_panel_init(lv_obj_t* panel){
y_offset = 0; lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, 0);
lv_layout_flex_column(panel);
lv_obj_set_scrollbar_mode(panel, LV_SCROLLBAR_MODE_OFF);
lv_obj_t * btn = lv_btn_create(panel); settings_section_theming(panel);
lv_obj_add_event_cb(btn, reset_wifi_click, LV_EVENT_CLICKED, NULL); settings_section_behaviour(panel);
settings_section_device(panel);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Restart");
lv_obj_center(label);
create_settings_widget("Configure WiFi", btn, panel);
btn = lv_btn_create(panel);
lv_obj_add_event_cb(btn, reset_calibration_click, LV_EVENT_CLICKED, NULL);
label = lv_label_create(btn);
lv_label_set_text(label, "Restart");
lv_obj_center(label);
create_settings_widget("Calibrate Touch", btn, panel);
lv_obj_t * toggle = lv_switch_create(panel);
lv_obj_add_event_cb(toggle, invert_color_switch, LV_EVENT_VALUE_CHANGED, NULL);
if (global_config.invertColors)
lv_obj_add_state(toggle, LV_STATE_CHECKED);
create_settings_widget("Invert Colors", toggle, panel);
toggle = lv_switch_create(panel);
lv_obj_add_event_cb(toggle, light_mode_switch, LV_EVENT_VALUE_CHANGED, NULL);
if (global_config.lightMode)
lv_obj_add_state(toggle, LV_STATE_CHECKED);
create_settings_widget("Light Mode", toggle, panel);
lv_obj_t * dropdown = lv_dropdown_create(panel);
lv_dropdown_set_options(dropdown, "Blue\nGreen\nGrey\nYellow\nOrange\nRed\nPurple");
lv_dropdown_set_selected(dropdown, global_config.color_scheme);
lv_obj_add_event_cb(dropdown, theme_dropdown, LV_EVENT_VALUE_CHANGED, NULL);
create_settings_widget("Theme", dropdown, panel);
dropdown = lv_dropdown_create(panel);
lv_dropdown_set_options(dropdown, brightness_options);
lv_obj_add_event_cb(dropdown, brightness_dropdown, LV_EVENT_VALUE_CHANGED, NULL);
for (int i = 0; i < SIZEOF(brightness_options_values); i++){
if (brightness_options_values[i] == global_config.brightness){
lv_dropdown_set_selected(dropdown, i);
break;
}
}
create_settings_widget("Brightness", dropdown, panel);
dropdown = lv_dropdown_create(panel);
lv_dropdown_set_options(dropdown, wake_timeout_options);
lv_obj_add_event_cb(dropdown, wake_timeout_dropdown, LV_EVENT_VALUE_CHANGED, NULL);
for (int i = 0; i < SIZEOF(wake_timeout_options_values); i++){
if (wake_timeout_options_values[i] == global_config.screenTimeout){
lv_dropdown_set_selected(dropdown, i);
break;
}
}
create_settings_widget("Wake Timeout", dropdown, panel);
toggle = lv_switch_create(panel);
lv_obj_add_event_cb(toggle, rotate_screen_switch, LV_EVENT_VALUE_CHANGED, NULL);
if (global_config.rotateScreen)
lv_obj_add_state(toggle, LV_STATE_CHECKED);
create_settings_widget("Rotate Screen", toggle, panel);
toggle = lv_switch_create(panel);
lv_obj_add_event_cb(toggle, on_during_print_switch, LV_EVENT_VALUE_CHANGED, NULL);
if (global_config.onDuringPrint)
lv_obj_add_state(toggle, LV_STATE_CHECKED);
create_settings_widget("Screen On During Print", toggle, panel);
} }

View File

@@ -0,0 +1,118 @@
#include "panel.h"
#include "../ui_utils.h"
#include "../nav_buttons.h"
#include <stdio.h>
#include <Esp.h>
#include "../../core/printer_integration.hpp"
static void swap_to_files_menu(lv_event_t * e) {
nav_buttons_setup(PANEL_FILES);
}
static void update_data(lv_event_t * e) {
lv_msg_send(DATA_PRINTER_DATA, get_current_printer());
}
void create_state_button(lv_obj_t * root, lv_event_cb_t label, lv_event_cb_t button){
lv_obj_t * btn = lv_btn_create(root);
lv_obj_set_size(btn, CYD_SCREEN_PANEL_WIDTH_PX / 2 - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, button, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(btn, update_data, LV_EVENT_CLICKED, NULL);
lv_obj_t * label_obj = lv_label_create(btn);
lv_obj_add_event_cb(label_obj, label, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label_obj, NULL);
lv_obj_align(label_obj, LV_ALIGN_CENTER, 0, 0);
}
static void label_pos(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char x_pos_buff[32];
sprintf(x_pos_buff, "X%.2f Y%.2f", get_current_printer_data()->position[0], get_current_printer_data()->position[1]);
lv_label_set_text(label, x_pos_buff);
}
static void label_filament_used_m(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char filament_buff[32];
sprintf(filament_buff, "%.2f m", get_current_printer_data()->filament_used_mm / 1000);
lv_label_set_text(label, filament_buff);
}
static void label_total_layers(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char layers_buff[32];
sprintf(layers_buff, "%d of %d", get_current_printer_data()->current_layer, get_current_printer_data()->total_layers);
lv_label_set_text(label, layers_buff);
}
static void label_pressure_advance(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char pressure_buff[32];
sprintf(pressure_buff, "%.3f (%.2fs)", get_current_printer_data()->pressure_advance, get_current_printer_data()->smooth_time);
lv_label_set_text(label, pressure_buff);
}
static void label_feedrate(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
char feedrate_buff[32];
sprintf(feedrate_buff, "%d mm/s", get_current_printer_data()->feedrate_mm_per_s);
lv_label_set_text(label, feedrate_buff);
}
void create_stat_text_block(lv_obj_t * root, const char* label, lv_event_cb_t value){
lv_obj_t * panel = lv_create_empty_panel(root);
lv_obj_set_size(panel, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_layout_flex_column(panel , LV_FLEX_ALIGN_START, CYD_SCREEN_GAP_PX / 2, CYD_SCREEN_GAP_PX / 2);
lv_obj_set_flex_align(panel, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_t * label_obj = lv_label_create(panel);
lv_label_set_text(label_obj, label);
lv_obj_set_style_text_font(label_obj, &CYD_SCREEN_FONT_SMALL, 0);
lv_obj_t * value_obj = lv_label_create(panel);
lv_obj_add_event_cb(value_obj, value, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, value_obj, NULL);
}
void stats_panel_init(lv_obj_t* panel) {
auto panel_width = CYD_SCREEN_PANEL_WIDTH_PX / 2 - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * left_panel = lv_create_empty_panel(panel);
lv_obj_set_size(left_panel, panel_width, CYD_SCREEN_PANEL_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_layout_flex_column(left_panel);
lv_obj_set_flex_align(left_panel, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_align(left_panel, LV_ALIGN_TOP_LEFT, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
lv_obj_clear_flag(left_panel, LV_OBJ_FLAG_SCROLLABLE);
create_stat_text_block(left_panel, "Position:", label_pos);
if (get_current_printer_data()->state != PrinterState::PrinterStateIdle){
create_stat_text_block(left_panel, "Filament Used:", label_filament_used_m);
create_stat_text_block(left_panel, "Layer:", label_total_layers);
}
create_stat_text_block(left_panel, "Pressure Advance:", label_pressure_advance);
create_stat_text_block(left_panel, "Feedrate:", label_feedrate);
lv_obj_t * right_panel = lv_create_empty_panel(panel);
lv_obj_set_size(right_panel, panel_width, CYD_SCREEN_PANEL_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_layout_flex_column(right_panel, LV_FLEX_ALIGN_CENTER);
lv_obj_align(right_panel, LV_ALIGN_TOP_RIGHT, -1 * CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
if (get_current_printer_data()->state >= PrinterState::PrinterStatePrinting){
lv_obj_t * btn = lv_btn_create(right_panel);
lv_obj_set_size(btn, CYD_SCREEN_PANEL_WIDTH_PX / 2 - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_event_cb(btn, swap_to_files_menu, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Files");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
BasePrinter* printer = get_current_printer();
for (int i = 0; i < printer->custom_menus_count; i++)
{
create_state_button(right_panel, (lv_event_cb_t)printer->custom_menus[i].set_label, (lv_event_cb_t)printer->custom_menus[i].open_panel);
}
}

View File

@@ -1,165 +1,378 @@
#include "lvgl.h" #include "lvgl.h"
#include "panel.h" #include "../../conf/global_config.h"
#include "../../core/data_setup.h"
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include "../ui_utils.h"
#include "../../core/printer_integration.hpp"
#include "../../core/current_printer.h"
// False: Hotend, True: Bed enum temp_target{
static bool hotend_or_bed = true; TARGET_HOTEND,
static char hotend_buff[40]; TARGET_BED,
static char bed_buff[40]; TARGET_HOTEND_CONFIG_1,
TARGET_HOTEND_CONFIG_2,
TARGET_HOTEND_CONFIG_3,
TARGET_BED_CONFIG_1,
TARGET_BED_CONFIG_2,
TARGET_BED_CONFIG_3,
};
static temp_target keyboard_target;
static bool temp_edit_mode = false;
lv_obj_t* root_panel;
static void update_printer_data_hotend_temp(lv_event_t * e){ static void update_printer_data_hotend_temp(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
sprintf(hotend_buff, "Hotend: %.0f C\nTarget: %.0f C", printer.extruder_temp, printer.extruder_target_temp); char hotend_buff[40];
sprintf(hotend_buff, "Hotend: %.0f C (Target: %.0f C)",
get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1],
get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1]);
lv_label_set_text(label, hotend_buff); lv_label_set_text(label, hotend_buff);
} }
static void update_printer_data_bed_temp(lv_event_t * e){ static void update_printer_data_bed_temp(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
sprintf(bed_buff, "Bed: %.0f C\nTarget: %.0f C", printer.bed_temp, printer.bed_target_temp); char bed_buff[40];
sprintf(bed_buff, "Bed: %.0f C (Target: %.0f C)",
get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed],
get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
lv_label_set_text(label, bed_buff); lv_label_set_text(label, bed_buff);
} }
static short get_temp_preset(int target){
switch (target){
case TARGET_HOTEND_CONFIG_1:
return get_current_printer()->printer_config->hotend_presets[0];
case TARGET_HOTEND_CONFIG_2:
return get_current_printer()->printer_config->hotend_presets[1];
case TARGET_HOTEND_CONFIG_3:
return get_current_printer()->printer_config->hotend_presets[2];
case TARGET_BED_CONFIG_1:
return get_current_printer()->printer_config->bed_presets[0];
case TARGET_BED_CONFIG_2:
return get_current_printer()->printer_config->bed_presets[1];
case TARGET_BED_CONFIG_3:
return get_current_printer()->printer_config->bed_presets[2];
default:
return -1;
}
}
static void update_temp_preset_label(lv_event_t * e){
lv_obj_t * label = lv_event_get_target(e);
int target = static_cast<int>(reinterpret_cast<intptr_t>(lv_event_get_user_data(e)));
short value = get_temp_preset(target);
String text_label = String(value) + " C";
lv_label_set_text(label, text_label.c_str());
}
void UpdateConfig(){
write_global_config();
lv_msg_send(DATA_PRINTER_TEMP_PRESET, get_current_printer());
}
static void keyboard_callback(lv_event_t * e){ static void keyboard_callback(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e); lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e); lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
if (code == LV_EVENT_READY) { const char * text = lv_textarea_get_text(ta);
const char * text = lv_textarea_get_text(ta);
int temp = atoi(text); int temp = atoi(text);
if (temp < 0 || temp > 500){ if (temp < 0 || temp > 500){
return; return;
}
Serial.printf("%d %s %d\n", hotend_or_bed, text, temp);
char gcode[64];
const char* space = "%20";
if (hotend_or_bed){
sprintf(gcode, "M140%sS%d", space, temp);
} else {
sprintf(gcode, "M104%sS%d", space, temp);
}
send_gcode(true, gcode);
} }
if(code == LV_EVENT_DEFOCUSED || code == LV_EVENT_CANCEL || code == LV_EVENT_READY) { switch (keyboard_target){
lv_keyboard_set_textarea(kb, NULL); case TARGET_HOTEND:
lv_obj_del(kb); current_printer_set_target_temperature(PrinterTemperatureDevice::PrinterTemperatureDeviceNozzle1, temp);
lv_obj_del(ta); break;
case TARGET_BED:
current_printer_set_target_temperature(PrinterTemperatureDevice::PrinterTemperatureDeviceBed, temp);
break;
case TARGET_HOTEND_CONFIG_1:
get_current_printer()->printer_config->hotend_presets[0] = temp;
UpdateConfig();
break;
case TARGET_HOTEND_CONFIG_2:
get_current_printer()->printer_config->hotend_presets[1] = temp;
UpdateConfig();
break;
case TARGET_HOTEND_CONFIG_3:
get_current_printer()->printer_config->hotend_presets[2] = temp;
UpdateConfig();
break;
case TARGET_BED_CONFIG_1:
get_current_printer()->printer_config->bed_presets[0] = temp;
UpdateConfig();
break;
case TARGET_BED_CONFIG_2:
get_current_printer()->printer_config->bed_presets[1] = temp;
UpdateConfig();
break;
case TARGET_BED_CONFIG_3:
get_current_printer()->printer_config->bed_presets[2] = temp;
UpdateConfig();
break;
} }
} }
static void show_keyboard(lv_event_t * e){
lv_obj_t * panel = (lv_obj_t *)lv_event_get_user_data(e);
lv_obj_t * keyboard = lv_keyboard_create(panel);
lv_obj_t * ta = lv_textarea_create(panel);
lv_obj_set_size(ta, 100, 30);
lv_obj_align(ta, LV_ALIGN_TOP_MID, 0, 40);
lv_textarea_set_max_length(ta, 3);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_text(ta, "");
lv_obj_add_event_cb(ta, keyboard_callback, LV_EVENT_ALL, keyboard);
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER);
lv_keyboard_set_textarea(keyboard, ta);
}
static void show_keyboard_with_hotend(lv_event_t * e){ static void show_keyboard_with_hotend(lv_event_t * e){
hotend_or_bed = false; keyboard_target = TARGET_HOTEND;
show_keyboard(e); lv_create_keyboard_text_entry(keyboard_callback, "Set Hotend Temp");
} }
static void show_keyboard_with_bed(lv_event_t * e){ static void show_keyboard_with_bed(lv_event_t * e){
hotend_or_bed = true; keyboard_target = TARGET_BED;
show_keyboard(e); lv_create_keyboard_text_entry(keyboard_callback, "Set Bed Temp");
} }
static void cooldown_temp(lv_event_t * e){ static void cooldown_temp(lv_event_t * e){
if (printer.state == PRINTER_STATE_PRINTING){ if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting){
return; return;
} }
send_gcode(true, "M104%20S0"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureCooldown);
send_gcode(true, "M140%20S0");
} }
static void btn_extrude(lv_event_t * e){ static void btn_extrude(lv_event_t * e){
if (printer.state == PRINTER_STATE_PRINTING){ if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting){
return; return;
} }
send_gcode(true, "M83"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureExtrude);
send_gcode(true, "G1%20E25%20F300"); }
static void set_temp_via_preset(lv_event_t * e){
int target = static_cast<int>(reinterpret_cast<intptr_t>(lv_event_get_user_data(e)));
int value = get_temp_preset(target);
if (temp_edit_mode) {
keyboard_target = (temp_target)target;
lv_create_keyboard_text_entry(keyboard_callback, "Set Preset Temp");
return;
}
current_printer_set_target_temperature(target <= TARGET_HOTEND_CONFIG_3
? PrinterTemperatureDevice::PrinterTemperatureDeviceNozzle1
: PrinterTemperatureDevice::PrinterTemperatureDeviceBed
, value);
}
static void btn_toggleable_edit(lv_event_t * e){
lv_obj_t * btn = lv_event_get_target(e);
auto state = lv_obj_get_state(btn);
temp_edit_mode = (state & LV_STATE_CHECKED == LV_STATE_CHECKED);
} }
static void btn_retract(lv_event_t * e){ static void btn_retract(lv_event_t * e){
if (printer.state == PRINTER_STATE_PRINTING){ if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting){
return; return;
} }
send_gcode(true, "M83"); current_printer_execute_feature(PrinterFeatures::PrinterFeatureRetract);
send_gcode(true, "G1%20E-25%20F300");
} }
void temp_panel_init(lv_obj_t* panel){ static void set_chart_range(lv_event_t * e) {
auto panel_width = TFT_HEIGHT - 40; lv_obj_t * chart_obj = lv_event_get_target(e);
lv_obj_t * label = lv_label_create(panel); lv_chart_t * chart = (lv_chart_t *)chart_obj;
lv_label_set_text(label, "Hotend"); int max_temp = 0;
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10); lv_chart_series_t * prev = NULL;
lv_obj_add_event_cb(label, update_printer_data_hotend_temp, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label, NULL); do {
prev = lv_chart_get_series_next(chart_obj, prev);
label = lv_label_create(panel); if (prev != NULL)
lv_label_set_text(label, "Bed"); for (int i = 0; i < chart->point_cnt; i++)
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 50); if (prev->y_points[i] > max_temp)
lv_obj_add_event_cb(label, update_printer_data_bed_temp, LV_EVENT_MSG_RECEIVED, NULL); max_temp = prev->y_points[i];
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label, NULL);
} while (prev != NULL);
lv_obj_t * btn = lv_btn_create(panel); int range = ((max_temp + 49) / 50) * 50;
lv_obj_align(btn, LV_ALIGN_TOP_RIGHT, -10, 10);
lv_obj_add_event_cb(btn, show_keyboard_with_hotend, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn); if (range < 100)
lv_label_set_text(label, "Set"); range = 100;
lv_obj_center(label);
btn = lv_btn_create(panel); lv_chart_set_range(chart_obj, LV_CHART_AXIS_PRIMARY_Y, 0, range);
lv_obj_align(btn, LV_ALIGN_TOP_RIGHT, -10, 50); }
lv_obj_add_event_cb(btn, show_keyboard_with_bed, LV_EVENT_CLICKED, panel);
label = lv_label_create(btn); static void set_hotend_temp_chart(lv_event_t * e){
lv_label_set_text(label, "Set"); lv_obj_t * chart = lv_event_get_target(e);
lv_obj_center(label); lv_chart_series_t * series = (lv_chart_series_t *)lv_event_get_user_data(e);
lv_chart_set_next_value(chart, series, get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1]);
}
btn = lv_btn_create(panel); static void set_hotend_target_temp_chart(lv_event_t * e){
lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, 90); lv_obj_t * chart = lv_event_get_target(e);
lv_obj_add_event_cb(btn, cooldown_temp, LV_EVENT_CLICKED, panel); lv_chart_series_t * series = (lv_chart_series_t *)lv_event_get_user_data(e);
lv_chart_set_next_value(chart, series, get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1]);
}
label = lv_label_create(btn); static void set_bed_temp_chart(lv_event_t * e){
lv_label_set_text(label, "Cooldown"); lv_obj_t * chart = lv_event_get_target(e);
lv_obj_center(label); lv_chart_series_t * series = (lv_chart_series_t *)lv_event_get_user_data(e);
lv_chart_set_next_value(chart, series, get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
}
btn = lv_btn_create(panel); static void set_bed_target_temp_chart(lv_event_t * e){
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 10, -5); lv_obj_t * chart = lv_event_get_target(e);
lv_chart_series_t * series = (lv_chart_series_t *)lv_event_get_user_data(e);
lv_chart_set_next_value(chart, series, get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
}
void create_charts(lv_obj_t * root)
{
const auto element_width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * chart = lv_chart_create(root);
lv_obj_set_size(chart, element_width - CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX * 3);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
lv_chart_set_point_count(chart, 120);
lv_obj_set_style_size(chart, 0, LV_PART_INDICATOR);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, CYD_SCREEN_GAP_PX / 2, CYD_SCREEN_GAP_PX / 4, 4, 3, true, CYD_SCREEN_MIN_BUTTON_WIDTH_PX);
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_ORANGE), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_all_value(chart, ser1, get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1]);
lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_all_value(chart, ser2, get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexNozzle1]);
lv_chart_series_t * ser3 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_TEAL), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_all_value(chart, ser3, get_current_printer_data()->target_temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
lv_chart_series_t * ser4 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_all_value(chart, ser4, get_current_printer_data()->temperatures[PrinterTemperatureDeviceIndex::PrinterTemperatureDeviceIndexBed]);
lv_obj_add_event_cb(chart, set_hotend_target_temp_chart, LV_EVENT_MSG_RECEIVED, ser1);
lv_obj_add_event_cb(chart, set_hotend_temp_chart, LV_EVENT_MSG_RECEIVED, ser2);
lv_obj_add_event_cb(chart, set_bed_target_temp_chart, LV_EVENT_MSG_RECEIVED, ser3);
lv_obj_add_event_cb(chart, set_bed_temp_chart, LV_EVENT_MSG_RECEIVED, ser4);
lv_obj_add_event_cb(chart, set_chart_range, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, chart, NULL);
}
void create_temp_buttons(lv_obj_t * root, lv_obj_t * panel)
{
const auto element_width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
lv_obj_t * temp_rows[2] = {0};
lv_obj_t * button_temp_rows[2] = {0};
for (int tempIter = 0; tempIter < 2; tempIter++){
temp_rows[tempIter] = lv_create_empty_panel(root);
lv_layout_flex_column(temp_rows[tempIter]);
lv_obj_set_size(temp_rows[tempIter], element_width, LV_SIZE_CONTENT);
lv_obj_t * label = lv_label_create(temp_rows[tempIter]);
lv_label_set_text(label, "???");
lv_obj_add_event_cb(label, (tempIter == 0) ? update_printer_data_hotend_temp : update_printer_data_bed_temp, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subscribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_set_width(label, element_width);
button_temp_rows[tempIter] = lv_create_empty_panel(temp_rows[tempIter]);
lv_layout_flex_row(button_temp_rows[tempIter], LV_FLEX_ALIGN_SPACE_EVENLY);
lv_obj_set_size(button_temp_rows[tempIter], element_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
for (int buttonIter = 0; buttonIter < 3; buttonIter++){
lv_obj_t * btn = lv_btn_create(button_temp_rows[tempIter]);
lv_obj_add_event_cb(btn, set_temp_via_preset, LV_EVENT_CLICKED, reinterpret_cast<void*>(TARGET_HOTEND_CONFIG_1 + buttonIter + tempIter * 3));
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, "???");
lv_obj_center(label);
lv_obj_add_event_cb(label, update_temp_preset_label, LV_EVENT_MSG_RECEIVED, reinterpret_cast<void*>(TARGET_HOTEND_CONFIG_1 + buttonIter + tempIter * 3));
lv_msg_subscribe_obj(DATA_PRINTER_TEMP_PRESET, label, NULL);
}
lv_obj_t * btn = lv_btn_create(button_temp_rows[tempIter]);
lv_obj_add_event_cb(btn, (tempIter == 0) ? show_keyboard_with_hotend : show_keyboard_with_bed, LV_EVENT_CLICKED, panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, "Set");
lv_obj_center(label);
}
}
void temp_panel_init(lv_obj_t * panel){
const auto element_width = CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2;
root_panel = panel;
temp_edit_mode = false;
lv_obj_t * root_temp_panel = lv_create_empty_panel(panel);
lv_obj_set_size(root_temp_panel, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
lv_obj_align(root_temp_panel, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_pad_all(root_temp_panel, CYD_SCREEN_GAP_PX, 0);
lv_layout_flex_column(root_temp_panel);
lv_obj_set_flex_align(root_temp_panel, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_CENTER);
lv_obj_set_scrollbar_mode(root_temp_panel, LV_SCROLLBAR_MODE_OFF);
#ifndef CYD_SCREEN_NO_TEMP_SCROLL
create_charts(root_temp_panel);
lv_obj_t * single_screen_panel = lv_create_empty_panel(root_temp_panel);
lv_obj_set_size(single_screen_panel, element_width, CYD_SCREEN_PANEL_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2 - CYD_SCREEN_GAP_PX / 2);
lv_layout_flex_column(single_screen_panel);
#else
lv_obj_clear_flag(root_temp_panel, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t * single_screen_panel = root_temp_panel;
#endif
create_temp_buttons(single_screen_panel, panel);
#ifdef CYD_SCREEN_NO_TEMP_SCROLL
create_charts(single_screen_panel);
#else
lv_obj_t * gap = lv_create_empty_panel(single_screen_panel);
lv_obj_set_flex_grow(gap, 1);
#endif
lv_obj_t * one_above_bottom_panel = lv_create_empty_panel(single_screen_panel);
lv_obj_set_size(one_above_bottom_panel, element_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(one_above_bottom_panel, LV_FLEX_ALIGN_SPACE_EVENLY);
lv_obj_t * bottom_panel = lv_create_empty_panel(single_screen_panel);
lv_obj_set_size(bottom_panel, element_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_layout_flex_row(bottom_panel, LV_FLEX_ALIGN_SPACE_EVENLY);
lv_obj_t * btn = lv_btn_create(bottom_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_extrude, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_extrude, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, panel_width / 2 - 15, 30); lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn); lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_DOWN " Extrude"); lv_label_set_text(label, LV_SYMBOL_DOWN " Extrude");
lv_obj_center(label); lv_obj_center(label);
btn = lv_btn_create(panel); btn = lv_btn_create(one_above_bottom_panel);
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -10, -5); lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_retract, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(btn, btn_retract, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, panel_width / 2 - 15, 30); lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn); label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_UP " Retract"); lv_label_set_text(label, LV_SYMBOL_UP " Retract");
lv_obj_center(label); lv_obj_center(label);
lv_msg_send(DATA_PRINTER_DATA, &printer); btn = lv_btn_create(bottom_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, cooldown_temp, LV_EVENT_CLICKED, NULL);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, "Cooldown");
lv_obj_center(label);
btn = lv_btn_create(one_above_bottom_panel);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, btn_toggleable_edit, LV_EVENT_CLICKED, NULL);
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
label = lv_label_create(btn);
lv_label_set_text(label, "Edit Presets");
lv_obj_center(label);
lv_obj_scroll_to_y(root_temp_panel, 9999, LV_ANIM_OFF);
lv_msg_send(DATA_PRINTER_TEMP_PRESET, get_current_printer());
} }

View File

@@ -0,0 +1,371 @@
#include "serial_commands.h"
#include <HardwareSerial.h>
#include <Esp.h>
#include <cstring>
#include "../../conf/global_config.h"
#include "../../core/printer_integration.hpp"
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
*/
PrinterConfiguration* get_current_printer_config()
{
return &global_config.printer_config[global_config.printer_index];
}
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()->printer_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()->printer_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()->printer_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()->printer_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()->printer_auth,0,64);
write_global_config();
}
else if(arg == "ip")
{
get_current_printer_config()->setup_complete = false;
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[])
{
get_current_printer_config()->auth_configured = true;
strncpy(get_current_printer_config()->printer_auth, argv[1].c_str(), sizeof(global_config.printer_config[0].printer_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()->printer_host, argv[1].c_str(), sizeof(global_config.printer_config[0].printer_host)-1);
get_current_printer_config()->klipper_port = argv[2].toInt();
get_current_printer_config()->ip_configured = true;
get_current_printer_config()->setup_complete = 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;
set_current_printer(0);
}
else if( ndx >= 0 && ndx < get_printer_count())
{
global_config.multi_printer_mode = true;
set_current_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,161 @@
#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
{
bool global_disable_serial_console = false;
/*
* 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;
// backspaceF
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] == ' ' && (index <= 0 || input[index - 1] != '\\')) || 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
{
if (input[index] != '\\')
{
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()
{
if (global_disable_serial_console)
return;
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,10 @@
namespace serial_console {
void greet();
void run();
extern bool global_disable_serial_console;
}

View File

@@ -0,0 +1,293 @@
#include "lvgl.h"
#include "ui_utils.h"
#include "../core/data_setup.h"
#include "../core/lv_setup.h"
#include "../core/printer_integration.hpp"
lv_obj_t* lv_create_empty_panel(lv_obj_t* root) {
lv_obj_t* panel = lv_obj_create(root);
lv_obj_set_style_border_width(panel, 0, 0);
lv_obj_set_style_bg_opa(panel, LV_OPA_TRANSP, 0);
lv_obj_set_style_pad_all(panel, 0, 0);
lv_obj_set_style_radius(panel, 0, 0);
return panel;
}
void lv_layout_flex_column(lv_obj_t* obj, lv_flex_align_t allign, lv_coord_t pad_column, lv_coord_t pad_row){
lv_obj_set_layout(obj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(obj, allign, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(obj, pad_column, 0);
lv_obj_set_style_pad_row(obj, pad_row, 0);
}
void lv_layout_flex_row(lv_obj_t* obj, lv_flex_align_t allign, lv_coord_t pad_column, lv_coord_t pad_row){
lv_obj_set_layout(obj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(obj, allign, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(obj, pad_column, 0);
lv_obj_set_style_pad_row(obj, pad_row, 0);
}
void destroy_event_user_data(lv_event_t * e){
lv_obj_t * obj = (lv_obj_t *)lv_event_get_user_data(e);
lv_obj_del(obj);
}
void destroy_event_free_data(lv_event_t * e)
{
void* data = lv_event_get_user_data(e);
free(data);
}
void lv_obj_on_destroy_free_data(lv_obj_t * element, const void* ptr)
{
lv_obj_add_event_cb(element, destroy_event_free_data, LV_EVENT_DELETE, (void*)ptr);
}
void lv_create_fullscreen_button_matrix_popup(lv_obj_t * root, lv_event_cb_t title, lv_button_column_t* columns, int column_count){
const auto full_panel_width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 3;
const auto full_panel_inner_width = full_panel_width - CYD_SCREEN_GAP_PX * 2 - 4;
const auto full_panel_height = CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX;
const auto full_panel_inner_height = full_panel_height - CYD_SCREEN_GAP_PX * 2 - 4;
auto column_width = full_panel_inner_width / column_count - CYD_SCREEN_GAP_PX;
auto column_height = full_panel_inner_height - CYD_SCREEN_GAP_PX - CYD_SCREEN_MIN_BUTTON_HEIGHT_PX;
lv_obj_t * panel = lv_obj_create(root);
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, 0);
lv_obj_set_size(panel, full_panel_width, full_panel_height);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * top_menu_row = lv_create_empty_panel(panel);
lv_obj_set_size(top_menu_row, full_panel_inner_width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(top_menu_row, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_t * btn = lv_btn_create(top_menu_row);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(btn, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, panel);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
lv_obj_center(label);
label = lv_label_create(top_menu_row);
lv_label_set_text(label, "-");
lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_add_event_cb(label, title, LV_EVENT_MSG_RECEIVED, NULL);
lv_msg_subsribe_obj(DATA_PRINTER_DATA, label, NULL);
lv_obj_t * rows = lv_create_empty_panel(panel);
lv_obj_set_size(rows, full_panel_inner_width, column_height);
lv_obj_align(rows, LV_ALIGN_BOTTOM_LEFT, 0, 0);
lv_layout_flex_row(rows, LV_FLEX_ALIGN_SPACE_BETWEEN, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
for (int i = 0; i < column_count; i++){
lv_obj_t * column = lv_create_empty_panel(rows);
lv_obj_clear_flag(column, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_size(column, column_width, column_height);
lv_layout_flex_column(column, LV_FLEX_ALIGN_CENTER, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
for (int j = 0; j < columns[i].length; j++){
lv_obj_t * btn = lv_btn_create(column);
lv_obj_set_width(btn, column_width);
lv_obj_set_flex_grow(btn, 1);
lv_obj_add_event_cb(btn, columns[i].event, LV_EVENT_CLICKED, (void*)columns[i].data[j]);
label = lv_label_create(btn);
lv_label_set_text(label, columns[i].labels[j]);
lv_obj_center(label);
}
}
}
void lv_keyboard_text_entry_close(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
if(code == LV_EVENT_DEFOCUSED || code == LV_EVENT_CANCEL || code == LV_EVENT_READY)
{
lv_keyboard_set_textarea(kb, NULL);
lv_obj_del(lv_obj_get_parent(kb));
}
}
void lv_create_keyboard_text_entry(lv_event_cb_t keyboard_callback, const char* title, lv_keyboard_mode_t keyboard_mode, lv_coord_t width, uint8_t max_length, const char* fill_text, bool contain_in_panel)
{
lv_obj_t * parent = lv_create_empty_panel(lv_scr_act());
lv_obj_set_style_bg_opa(parent, LV_OPA_50, 0);
lv_obj_align(parent, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_layout_flex_column(parent, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_obj_clear_flag(parent, LV_OBJ_FLAG_SCROLLABLE);
if (contain_in_panel)
{
lv_obj_set_size(parent, CYD_SCREEN_PANEL_WIDTH_PX, CYD_SCREEN_PANEL_HEIGHT_PX);
}
else
{
lv_obj_set_size(parent, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
}
if (title != nullptr && keyboard_mode != LV_KEYBOARD_MODE_NUMBER)
{
lv_obj_t * empty_panel = lv_create_empty_panel(parent);
lv_obj_set_size(empty_panel, 0, 0);
lv_obj_t * title_container = lv_obj_create(parent);
lv_obj_set_style_pad_all(title_container, CYD_SCREEN_GAP_PX / 2, 0);
lv_obj_set_size(title_container, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_t * title_label = lv_label_create(title_container);
lv_label_set_text(title_label, title);
}
lv_obj_t * empty_panel = lv_create_empty_panel(parent);
lv_obj_set_flex_grow(empty_panel, 1);
lv_obj_t * ta = lv_textarea_create(parent);
lv_obj_t * keyboard = lv_keyboard_create(parent);
if (keyboard_mode == LV_KEYBOARD_MODE_NUMBER)
{
lv_textarea_set_placeholder_text(ta, title);
lv_obj_set_height(keyboard, LV_PCT(75));
}
lv_obj_set_width(ta, width);
lv_textarea_set_max_length(ta, max_length);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_text(ta, fill_text);
lv_obj_add_event_cb(ta, keyboard_callback, LV_EVENT_READY, keyboard);
lv_obj_add_event_cb(ta, lv_keyboard_text_entry_close, LV_EVENT_ALL, keyboard);
lv_keyboard_set_mode(keyboard, keyboard_mode);
lv_keyboard_set_textarea(keyboard, ta);
}
const static lv_point_t line_points[] = { {0, 0}, {(short int)((CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2) * 0.85f), 0} };
void lv_create_custom_menu_entry(const char* label_text, lv_obj_t* object, lv_obj_t* root_panel, bool set_height, const char * comment)
{
lv_obj_t * panel = lv_create_empty_panel(root_panel);
lv_layout_flex_row(panel, LV_FLEX_ALIGN_END);
lv_obj_set_size(panel, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 3, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t * label = lv_label_create(panel);
lv_label_set_text(label, label_text);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
lv_obj_set_parent(object, panel);
if (set_height)
{
lv_obj_set_height(object, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
}
if (comment != NULL)
{
lv_obj_t * comment_label = lv_label_create(root_panel);
lv_label_set_text(comment_label, comment);
lv_obj_set_style_text_font(comment_label, &CYD_SCREEN_FONT_SMALL, 0);
}
lv_obj_t * line = lv_line_create(root_panel);
lv_line_set_points(line, line_points, 2);
lv_obj_set_style_line_width(line, 1, 0);
lv_obj_set_style_line_color(line, lv_color_hex(0xAAAAAA), 0);
}
#define DROPDOWN_WIDTH CYD_SCREEN_MIN_BUTTON_WIDTH_PX * 3.75
#define TOGGLE_WIDTH CYD_SCREEN_MIN_BUTTON_WIDTH_PX * 2
void lv_create_custom_menu_button(const char *label_text, lv_obj_t* root_panel, lv_event_cb_t on_click, const char *btn_text, void * user_data, const char * comment)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn, on_click, LV_EVENT_CLICKED, user_data);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, btn_text);
lv_obj_center(label);
lv_create_custom_menu_entry(label_text, btn, root_panel, true, comment);
}
void lv_create_custom_menu_switch(const char *label_text, lv_obj_t* root_panel, lv_event_cb_t on_toggle, bool state, void * user_data, const char * comment)
{
lv_obj_t * toggle = lv_switch_create(lv_scr_act());
lv_obj_add_event_cb(toggle, on_toggle, LV_EVENT_VALUE_CHANGED, user_data);
lv_obj_set_width(toggle, TOGGLE_WIDTH);
if (state)
lv_obj_add_state(toggle, LV_STATE_CHECKED);
lv_create_custom_menu_entry(label_text, toggle, root_panel, true, comment);
}
void lv_create_custom_menu_dropdown(const char *label_text, lv_obj_t *root_panel, lv_event_cb_t on_change, const char *options, int index, void * user_data, const char * comment)
{
lv_obj_t * dropdown = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options(dropdown, options);
lv_dropdown_set_selected(dropdown, index);
lv_obj_set_width(dropdown, DROPDOWN_WIDTH);
lv_obj_add_event_cb(dropdown, on_change, LV_EVENT_VALUE_CHANGED, user_data);
lv_create_custom_menu_entry(label_text, dropdown, root_panel, true, comment);
}
void lv_create_custom_menu_label(const char *label_text, lv_obj_t* root_panel, const char *text)
{
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, text);
lv_create_custom_menu_entry(label_text, label, root_panel, false);
}
lv_timer_t* timer = NULL;
void on_timer_destroy(lv_event_t * e)
{
lv_timer_del(timer);
timer = NULL;
}
void timer_callback(lv_timer_t *timer)
{
lv_obj_t * panel = (lv_obj_t *)timer->user_data;
lv_obj_del(panel);
}
void lv_create_popup_message(const char* message, uint16_t timeout_ms)
{
if (message == nullptr || timer != NULL)
{
return;
}
lv_obj_t* panel = lv_obj_create(lv_scr_act());
lv_obj_set_style_pad_all(panel, CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
lv_obj_set_size(panel, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_layout_flex_column(panel, LV_FLEX_ALIGN_CENTER);
lv_obj_align(panel, LV_ALIGN_TOP_RIGHT, -CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX);
lv_obj_add_event_cb(panel, on_timer_destroy, LV_EVENT_DELETE, NULL);
lv_obj_add_event_cb(panel, destroy_event_user_data, LV_EVENT_CLICKED, panel);
lv_obj_set_style_border_color(panel, lv_color_hex(0xFF0000), 0);
lv_obj_t* label = lv_label_create(panel);
lv_label_set_text_fmt(label, "%s", message);
lv_obj_set_size(label, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 6, LV_SIZE_CONTENT);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
timer = lv_timer_create(timer_callback, timeout_ms > 0 ? timeout_ms : 0xFFFFFFFF, panel);
}
lv_obj_t * lv_label_btn_create(lv_obj_t * parent, lv_event_cb_t btn_callback, void* user_data)
{
lv_obj_t * panel = lv_create_empty_panel(parent);
lv_obj_set_size(panel, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_add_event_cb(panel, btn_callback, LV_EVENT_CLICKED, user_data);
lv_obj_t * label = lv_label_create(panel);
return label;
}

View File

@@ -0,0 +1,49 @@
#pragma once
#ifdef CYD_SCREEN_VERTICAL
#ifndef CYD_SCREEN_WIDTH_PX
#define CYD_SCREEN_WIDTH_PX LCD_WIDTH
#endif
#ifndef CYD_SCREEN_HEIGHT_PX
#define CYD_SCREEN_HEIGHT_PX LCD_HEIGHT
#endif
#define CYD_SCREEN_PANEL_HEIGHT_PX \
(CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_SIDEBAR_SIZE_PX)
#define CYD_SCREEN_PANEL_WIDTH_PX CYD_SCREEN_WIDTH_PX
#else
#ifndef CYD_SCREEN_WIDTH_PX
#define CYD_SCREEN_WIDTH_PX LCD_HEIGHT
#endif
#ifndef CYD_SCREEN_HEIGHT_PX
#define CYD_SCREEN_HEIGHT_PX LCD_WIDTH
#endif
#define CYD_SCREEN_PANEL_HEIGHT_PX CYD_SCREEN_HEIGHT_PX
#define CYD_SCREEN_PANEL_WIDTH_PX \
(CYD_SCREEN_WIDTH_PX - CYD_SCREEN_SIDEBAR_SIZE_PX)
#endif
typedef struct {
lv_event_cb_t event;
const char** labels;
const void** data;
int length;
} lv_button_column_t;
lv_obj_t* lv_create_empty_panel(lv_obj_t* root);
void lv_layout_flex_column(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX);
void lv_layout_flex_row(lv_obj_t* obj, lv_flex_align_t allign = LV_FLEX_ALIGN_START, lv_coord_t pad_column = CYD_SCREEN_GAP_PX, lv_coord_t pad_row = CYD_SCREEN_GAP_PX);
void lv_create_fullscreen_button_matrix_popup(lv_obj_t * root, lv_event_cb_t title, lv_button_column_t* columns, int column_count);
void destroy_event_user_data(lv_event_t * e);
void lv_obj_on_destroy_free_data(lv_obj_t * element, const void* ptr);
void lv_create_keyboard_text_entry(lv_event_cb_t keyboard_callback, const char* title = NULL, lv_keyboard_mode_t keyboard_mode = LV_KEYBOARD_MODE_NUMBER, lv_coord_t width = LV_PCT(75), uint8_t max_length = 3, const char* fill_text = "", bool contain_in_panel= true);
void lv_create_custom_menu_entry(const char* label_text, lv_obj_t* object, lv_obj_t* root_panel, bool set_height = true, const char * comment = NULL);
void lv_create_custom_menu_button(const char *label_text, lv_obj_t* root_panel, lv_event_cb_t on_click, const char *btn_text, void * user_data = NULL, const char * comment = NULL);
void lv_create_custom_menu_switch(const char *label_text, lv_obj_t* root_panel, lv_event_cb_t on_toggle, bool state, void * user_data = NULL, const char * comment = NULL);
void lv_create_custom_menu_dropdown(const char *label_text, lv_obj_t *root_panel, lv_event_cb_t on_change, const char *options, int index, void * user_data = NULL, const char * comment = NULL);
void lv_create_custom_menu_label(const char *label_text, lv_obj_t* root_panel, const char *text);
void lv_create_popup_message(const char* message, uint16_t timeout_ms);
lv_obj_t * lv_label_btn_create(lv_obj_t * parent, lv_event_cb_t btn_callback, void* user_data = NULL);

View File

@@ -1,95 +1,160 @@
#include "lvgl.h" #include "lvgl.h"
#include "wifi_setup.h" #include "wifi_setup.h"
#include "../conf/global_config.h" #include "../conf/global_config.h"
#include "ui_utils.h"
#include "WiFi.h" #include "WiFi.h"
#include "../core/data_setup.h"
#include "../core/lv_setup.h"
#include "serial/serial_console.h"
#include "panels/panel.h"
#include "../core/semaphore.h"
void wifi_init_inner(); void wifi_init_inner();
void wifi_pass_entry(const char* ssid);
const char * current_ssid_ptr = NULL;
static void reset_btn_event_handler(lv_event_t * e) { static void reset_btn_event_handler(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e); global_config.wifi_configured = false;
wifi_init_inner();
if(code == LV_EVENT_CLICKED) {
global_config.wifiConfigured = false;
wifi_init_inner();
}
} }
static void refresh_btn_event_handler(lv_event_t * e){ static void keyboard_cb_enter_password(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
wifi_init_inner();
}
}
static void ta_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e); lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
if (code == LV_EVENT_READY) const char * txt = lv_textarea_get_text(ta);
{ global_config.wifi_configured = true;
const char * txt = lv_textarea_get_text(ta); strcpy(global_config.wifi_SSID, current_ssid_ptr);
int len = strlen(txt); strcpy(global_config.wifi_password, txt);
if (len > 0) write_global_config();
{ wifi_init_inner();
global_config.wifiConfigured = true;
strcpy(global_config.wifiPassword, txt);
WriteGlobalConfig();
wifi_init_inner();
}
}
else if (code == LV_EVENT_CANCEL)
{
wifi_init_inner();
}
} }
void wifi_pass_entry(const char* ssid){ static void btn_reuse_password(lv_event_t * e)
lv_obj_clean(lv_scr_act()); {
ESP.restart();
}
lv_obj_t * label = lv_label_create(lv_scr_act()); static void btn_no_reuse_password(lv_event_t * e)
lv_label_set_text(label, "Enter WiFi Password"); {
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10 + 2); lv_create_keyboard_text_entry(keyboard_cb_enter_password, "Enter WiFi Password", LV_KEYBOARD_MODE_TEXT_LOWER, CYD_SCREEN_WIDTH_PX * 0.75, 63, "", false);
}
lv_obj_t * passEntry = lv_textarea_create(lv_scr_act()); void ask_reuse_password(const char * ssid){
lv_textarea_set_one_line(passEntry, true); lv_obj_t * root = lv_obj_create(lv_scr_act());
lv_textarea_set_text(passEntry, ""); lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_HEIGHT_PX - CYD_SCREEN_GAP_PX * 2);
lv_obj_align(passEntry, LV_ALIGN_TOP_LEFT, 10, 40); lv_obj_align(root, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(passEntry, ta_event_cb, LV_EVENT_ALL, NULL); lv_layout_flex_column(root, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_obj_set_size(passEntry, TFT_HEIGHT - 20, 60);
lv_obj_t * keyboard = lv_keyboard_create(lv_scr_act()); lv_obj_t * label = lv_label_create(root);
lv_keyboard_set_textarea(keyboard, passEntry); lv_label_set_text_fmt(label, "Reuse stored WiFi Password?\n(Password Length: %d)", strlen(global_config.wifi_password));
lv_obj_t * button_row = lv_create_empty_panel(root);
lv_layout_flex_row(button_row, LV_FLEX_ALIGN_SPACE_BETWEEN);
lv_obj_set_size(button_row, lv_pct(100), CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t * no_btn = lv_btn_create(button_row);
lv_obj_add_event_cb(no_btn, btn_no_reuse_password, LV_EVENT_CLICKED, (void*)ssid);
lv_obj_add_event_cb(no_btn, destroy_event_user_data, LV_EVENT_CLICKED, root);
lv_obj_set_height(no_btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_set_width(no_btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX);
lv_obj_set_style_pad_all(no_btn, CYD_SCREEN_GAP_PX, 0);
label = lv_label_create(no_btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
lv_obj_center(label);
lv_obj_t * yes_btn = lv_btn_create(button_row);
lv_obj_add_event_cb(yes_btn, btn_reuse_password, LV_EVENT_CLICKED, (void*)ssid);
lv_obj_set_height(yes_btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_set_width(yes_btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX);
lv_obj_set_style_pad_all(yes_btn, CYD_SCREEN_GAP_PX, 0);
label = lv_label_create(yes_btn);
lv_label_set_text(label, LV_SYMBOL_OK);
lv_obj_center(label);
}
void wifi_pass_entry(const char* ssid)
{
current_ssid_ptr = ssid;
if (strcmp(ssid, global_config.wifi_SSID) == 0){
ask_reuse_password(ssid);
return;
}
btn_no_reuse_password(NULL);
} }
static void wifi_btn_event_handler(lv_event_t * e){ static void wifi_btn_event_handler(lv_event_t * e){
lv_event_code_t code = lv_event_get_code(e); delay(100);
char* ssid = (char*)e->user_data;
if(code == LV_EVENT_CLICKED) { LOG_LN(ssid);
delay(100); wifi_pass_entry(ssid);
char* ssid = (char*)e->user_data;
strcpy(global_config.wifiSSID, ssid);
Serial.println(ssid);
wifi_pass_entry(ssid);
}
} }
static void wifi_keyboard_cb_manual_ssid(lv_event_t * e){
lv_obj_t * ta = lv_event_get_target(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
const char * text = lv_textarea_get_text(ta);
char * text_copy = (char*)malloc(strlen(text) + 1);
strcpy(text_copy, text);
LOG_LN(text_copy);
wifi_pass_entry(text_copy);
}
static void wifi_btn_manual_ssid(lv_event_t * e){
lv_create_keyboard_text_entry(wifi_keyboard_cb_manual_ssid, "Enter SSID Manually", LV_KEYBOARD_MODE_TEXT_LOWER, CYD_SCREEN_WIDTH_PX * 0.75, 31, "", false);
}
static void wifi_btn_skip_setup(lv_event_t * e){
global_config.wifi_configuration_skipped = true;
write_global_config();
}
static void wifi_btn_settings(lv_event_t * e){
lv_obj_clean(lv_scr_act());
lv_obj_t * panel = lv_create_empty_panel(lv_scr_act());
lv_obj_set_size(panel, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(panel);
lv_obj_t * btn = lv_btn_create(panel);
lv_obj_add_event_cb(btn, reset_btn_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_set_style_radius(btn, 0, 0);
lv_obj_set_size(btn, LV_PCT(100), CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Return to WiFi Setup");
lv_obj_center(label);
settings_section_device(panel);
}
void wifi_init_inner(){ void wifi_init_inner(){
WiFi.disconnect(); WiFi.disconnect();
lv_obj_clean(lv_scr_act());
if (global_config.wifiConfigured){ if (global_config.wifi_configured){
WiFi.begin(global_config.wifiSSID, global_config.wifiPassword); if (global_config.wifi_password[0] == '\0')
{
WiFi.begin(global_config.wifi_SSID);
}
else
{
WiFi.begin(global_config.wifi_SSID, global_config.wifi_password);
}
lv_obj_clean(lv_scr_act()); 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");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * resetBtn = lv_btn_create(lv_scr_act()); lv_obj_t * resetBtn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(resetBtn, reset_btn_event_handler, LV_EVENT_ALL, NULL); lv_obj_add_event_cb(resetBtn, reset_btn_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_align(resetBtn, LV_ALIGN_CENTER, 0, 40); lv_obj_set_height(resetBtn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
lv_obj_align(resetBtn, LV_ALIGN_CENTER, 0, CYD_SCREEN_GAP_PX + CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(resetBtn); label = lv_label_create(resetBtn);
lv_label_set_text(label, "Reset"); lv_label_set_text(label, "Reset");
@@ -98,8 +163,6 @@ void wifi_init_inner(){
return; return;
} }
lv_obj_clean(lv_scr_act());
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, "Scanning for networks..."); lv_label_set_text(label, "Scanning for networks...");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
@@ -108,52 +171,140 @@ void wifi_init_inner(){
lv_task_handler(); lv_task_handler();
lv_refr_now(NULL); lv_refr_now(NULL);
int n = WiFi.scanNetworks();
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
lv_obj_t * refreshBtn = lv_btn_create(lv_scr_act()); lv_obj_t * root = lv_create_empty_panel(lv_scr_act());
lv_obj_add_event_cb(refreshBtn, reset_btn_event_handler, LV_EVENT_ALL, NULL); lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_obj_align(refreshBtn, LV_ALIGN_TOP_RIGHT, -5, 5 - 1); lv_layout_flex_column(root);
lv_obj_set_style_pad_all(root, CYD_SCREEN_GAP_PX, 0);
label = lv_label_create(refreshBtn); lv_obj_t * top_row = lv_create_empty_panel(root);
lv_obj_set_size(top_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_layout_flex_row(top_row);
label = lv_label_create(top_row);
lv_label_set_text(label, "Select a network");
lv_obj_set_flex_grow(label, 1);
lv_obj_t * btn = lv_btn_create(top_row);
lv_obj_add_event_cb(btn, wifi_btn_settings, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_SETTINGS);
lv_obj_center(label);
btn = lv_btn_create(top_row);
lv_obj_add_event_cb(btn, wifi_btn_skip_setup, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX * 1.5, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, "Skip");
lv_obj_center(label);
btn = lv_btn_create(top_row);
lv_obj_add_event_cb(btn, wifi_btn_manual_ssid, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_KEYBOARD);
lv_obj_center(label);
btn = lv_btn_create(top_row);
lv_obj_add_event_cb(btn, reset_btn_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX);
label = lv_label_create(btn);
lv_label_set_text(label, LV_SYMBOL_REFRESH); lv_label_set_text(label, LV_SYMBOL_REFRESH);
lv_obj_center(label); lv_obj_center(label);
label = lv_label_create(lv_scr_act()); lv_obj_t * list = lv_list_create(root);
lv_label_set_text(label, "Select a network"); lv_obj_set_width(list, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2);
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10 + 2); lv_obj_set_flex_grow(list, 1);
lv_obj_t * list = lv_list_create(lv_scr_act()); int n = WiFi.scanNetworks();
lv_obj_align(list, LV_ALIGN_TOP_LEFT, 10, 40);
lv_obj_set_size(list, TFT_HEIGHT - 20, TFT_WIDTH - 40 - 5);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
const char* ssid = WiFi.SSID(i).c_str(); String ssid = WiFi.SSID(i);
int len = strlen(ssid); char* ssid_copy = (char*)malloc(ssid.length() + 1);
int j = 0;
if (len == 0) for (; j < ssid.length(); ++j){
continue; if (ssid[j] == '\0')
continue;
ssid_copy[j] = ssid[j];
}
ssid_copy[j] = '\0';
const char* ssid_copy = (const char*)malloc(len + 1);
strcpy((char*)ssid_copy, ssid);
lv_obj_t * btn = lv_list_add_btn(list, LV_SYMBOL_WIFI, ssid_copy); lv_obj_t * btn = lv_list_add_btn(list, LV_SYMBOL_WIFI, ssid_copy);
lv_obj_add_event_cb(btn, wifi_btn_event_handler, LV_EVENT_ALL, (void*)ssid_copy); lv_obj_add_event_cb(btn, wifi_btn_event_handler, LV_EVENT_CLICKED, (void*)ssid_copy);
} }
} }
const char* errs[] = {
"Idle",
"No SSID Available",
"Scan Completed",
"Connected",
"Connection Failed",
"Connection Lost",
"Disconnected"
};
const int print_freq = 1000;
int print_timer = 0;
void wifi_init(){ void wifi_init(){
if (global_config.wifi_configuration_skipped)
{
return;
}
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
wifi_init_inner(); wifi_init_inner();
while (!global_config.wifiConfigured || WiFi.status() != WL_CONNECTED){ while (!global_config.wifi_configuration_skipped && (!global_config.wifi_configured || WiFi.status() != WL_CONNECTED)){
lv_timer_handler(); if (millis() - print_timer > print_freq){
lv_task_handler(); print_timer = millis();
LOG_F(("WiFi Status: %s\n", errs[WiFi.status()]))
}
lv_handler();
serial_console::run();
} }
} }
ulong start_time_recovery = 0;
void wifi_ok(){ void wifi_ok(){
if (WiFi.status() != WL_CONNECTED){ if (global_config.wifi_configured && WiFi.status() != WL_CONNECTED){
ESP.restart(); LOG_LN("WiFi Connection Lost. Reconnecting...");
freeze_request_thread();
WiFi.disconnect();
delay(5000); // Wait for the WiFi to disconnect
start_time_recovery = millis();
if (global_config.wifi_password[0] == '\0')
{
WiFi.begin(global_config.wifi_SSID);
}
else
{
WiFi.begin(global_config.wifi_SSID, global_config.wifi_password);
}
while (WiFi.status() != WL_CONNECTED){
delay(1000);
LOG_F(("WiFi Status: %s\n", errs[WiFi.status()]))
if (millis() - start_time_recovery > 15000){
LOG_LN("WiFi Connection failed to reconnect. Restarting...");
ESP.restart();
}
}
unfreeze_request_thread();
} }
} }

48
Manual_Install.md Normal file
View File

@@ -0,0 +1,48 @@
# Manual install
## Flashing a build via the commandline
#### Release firmware image
1. Go to [the releases tab](https://github.com/suchmememanyskill/CYD-Klipper/releases), and download the latest `firmware.zip` release.
2. Open the firmware.zip archive you just downloaded, open the corresponding folder for your screen, then extract `merged_firmware.bin`
This merged_firmware.bin file is a ready to flash image. Note that flashing this image does wipe your current configuration.
#### Developer firmware image
On every change made in this repository, [Github Actions](https://github.com/suchmememanyskill/CYD-Klipper/actions) automatically builds the firmware images for various screens.
0. Make sure you are logged into github
1. Go to a [Github Actions](https://github.com/suchmememanyskill/CYD-Klipper/actions) build summary, then download the `firmware` Artifact.
2. Open the firmware.zip archive you just downloaded, open the corresponding folder for your screen, then extract `merged_firmware.bin`
This merged_firmware.bin file is a ready to flash image. Note that flashing this image does wipe your current configuration.
### Flashing merged_firmware.bin
1. Download and extract [esptool](https://github.com/espressif/esptool/releases) to a new folder
- For windows, download `esptool-v4.7.0-win64.zip`
2. Open a terminal window and navigate to this new folder containing esptool
- In windows, you can type cmd and press enter in the address bar in explorer to jump to the folder in cmd ![cmd](readme/cmd.jpg)
3. Move `merged-firmware.bin` into this new folder
4. Connect the display to your computer
5. Execute the command `esptool write_flash 0x0 merged_firmware.bin`
- Don't forget to hold down the boot button on the display
## Building & Flashing via PlatformIO
0. Install the following tools:
- [Visual Studio Code](https://code.visualstudio.com/) and install the PlatformIO IDE plugin.
- [Git](https://git-scm.com/download/win)
1. Download the source code of CYD-Klipper
- This can be done via the `git clone https://github.com/suchmememanyskill/CYD-Klipper` command or via the green `<> Code` button on Github
2. Open the CYD-Klipper folder inside the CYD-Klipper folder in Visual Studio Code
3. Click on the Alien/Bug tab (PlatformIO) on the left
4. Expand the folder/tab for your specific screen
- Entries with the suffix '-SD' are using the smartdisplay driver. Entries without this suffix are using a custom driver
- Usually, a custom driver is preferred over the smartdisplay driver
5. Connect the display to your computer
6. Click 'Upload and Monitor'
- This will start compiling the code, and after upload it to the display
- Don't forget to hold the boot button while flashing. The screen will flash when the firmware is attempted to be flashed
![platformio](readme/platformio.png)

View File

@@ -2,9 +2,10 @@
[![Donations](https://img.shields.io/badge/Support%20on-Ko--Fi-red)](https://ko-fi.com/suchmememanyskill) [![Donations](https://img.shields.io/badge/Support%20on-Ko--Fi-red)](https://ko-fi.com/suchmememanyskill)
# CYD-Klipper # CYD-Klipper
An implementation of a Klipper status display on an ESP32 + screen. Uses Moonraker to fetch data. An implementation of a wireless Klipper, Bambu and Octoprint status display on an ESP32 + screen. Uses Moonraker to fetch data.
A simple and cheap solution to use a dedicated screen with Klipper, a 3d printing Firmware. A simple and cheap solution to use a dedicated screen with Klipper, a 3d printing Firmware.
Also now with Bambu Lab and Octoprint printers!
![showcase_image](readme/PXL_20231113_171629383.jpg) ![showcase_image](readme/PXL_20231113_171629383.jpg)
@@ -14,30 +15,62 @@ A ESP32-2432S028R is required to run this project. You can find out where to buy
### Features ### Features
- View printer status - View printer status
- View print progress - View print progress and print statistics
- Start a print - Start a print
- Move the printer - (When the printer is idle) move the printer
- (During a print) set fan speed, flow rate, speed and z offset
- Manage temperature - Manage temperature
- Extrude/Retract filament - Extrude/Retract filament
- Execute predefined gcode macros
- Toggle Moonraker power devices
- OTA updates
- Serial console over USB (115200 8n1, echo off, LF/LF)
- Control Klipper, Octoprint and Bambu printers.
- Wired Serial/Usb Klipper connection
### 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!
### Where to buy hardware
All links below are affiliate links. Please also check yourself if there is a cheaper version available than the ones below. I have only linked ones that i have personally bought.
*ESP32-2432S028R (2.8" Resistive, Cheapest)*
- [USB C + microB version](https://s.click.aliexpress.com/e/_omjsYBJ)
- [Another USB C + microB version](https://s.click.aliexpress.com/e/_olKBkmz)
- [microB version](https://s.click.aliexpress.com/e/_oCWhgmN)
*ESP32-2432S032C (3.2" Capacitive)*
- [Only the capacitive version is supported! USB-C](https://s.click.aliexpress.com/e/_okbSGmd)
- [IPS version (not that great of a screen), Only the capacitive version is supported! USB-C](https://s.click.aliexpress.com/e/_oFygVwt)
*ESP32-3248S035C (3.5" Capacitive)*
- [microB version](https://s.click.aliexpress.com/e/_oCqygE9)
### Screenshots ### Screenshots
(Quite literally shots of the screen. I'm sorry) (Quite literally shots of the screen. I'm sorry)
-|- -|-
:-:|:-: :-:|:-:
![1](readme/PXL_20231113_142717308.jpg)|![2](readme/PXL_20231113_171701876.jpg) ![1](readme/1.jpg)|![2](readme/2.jpg)
![3](readme/PXL_20231113_171715809.jpg)|![4](readme/PXL_20231113_171724404.jpg) ![3](readme/3.jpg)|![4](readme/4.jpg)
![5](readme/PXL_20231113_171751745.jpg)|![6](readme/PXL_20231113_171809315.jpg) ![5](readme/5.jpg)|![6](readme/6.jpg)
![7](readme/7.jpg)|![8](readme/8.jpg)
![9](readme/9.jpg)|![10](readme/10.jpg)
### Credits ### Credits
- [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
- [esp32-smartdisplay](https://github.com/rzeldent/esp32-smartdisplay)

View File

@@ -8,25 +8,149 @@
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
} }
TT {
font-family: 'Terminal', monospace;
background-color: #080a0b;
}
body {
background-color: #181a1b;
color: white;
}
.main { .main {
width: fit-content; width: fit-content;
margin: auto; margin: auto;
max-width: 750px;
} }
.main > section > :not(:first-child),
.indent {
margin-left: 20px;
}
.main a {
color: #F44;
}
.configure {
margin-bottom: 300px;
}
.install .iconify {
color: green;
filter: drop-shadow(0 0 0.75rem lime);
}
#changelog-body {
white-space: break-spaces;
}
.where-to-get details summary::marker
{
content: '';
}
.where-to-get details[open] summary p
{
display: none;
}
</style> </style>
<script type="module" src="https://unpkg.com/esp-web-tools@9/dist/web/install-button.js?module"></script> <script type="module" src="https://unpkg.com/esp-web-tools@9/dist/web/install-button.js?module"></script>
<script src="//code.iconify.design/1/1.0.6/iconify.min.js"></script>
<script>
async function fetchChangelog() {
const response = await fetch("https://api.github.com/repos/suchmememanyskill/CYD-Klipper/releases/latest");
const data = await response.json();
document.getElementById("changelog-body").innerText = data.body;
document.getElementById("changelog-header-version").innerText += data.tag_name;
}
function setInstallButton(esp){
document.getElementById("install-btn").innerHTML = `<esp-web-install-button manifest="https://suchmememanyskill.github.io/CYD-Klipper/${esp}.json"></esp-web-install-button>`;
}
function setInstallButtonDefault(){
setInstallButton("esp32-2432S028R")
}
fetchChangelog();
window.onload = setInstallButtonDefault;
</script>
</head> </head>
<body> <body>
<section class="main"> <section class="main">
<h2>CYD-Klipper</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.</p> <p>An implementation of a wireless Klipper, Bambu and Octoprint 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>
<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> <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>
<p id="changelog-body"></p>
</section>
<section class="donate">
<h3><span class="iconify" data-icon="mdi-heart" style="color:orangered; filter: drop-shadow(0 0 0.75rem crimson);"></span> Donate</h3>
<p>If you found this project helpful, please consider a donation to <a href="https://ko-fi.com/suchmememanyskill">my Ko-Fi</a>.<br>It would help out a lot in the development of this project, due to the need to buy the screens.<br>Thank you!</p>
</section>
<section class="where-to-get">
<details>
<summary>
<h3><span class="iconify" data-icon="mdi-shopping" style="color:orange; filter: drop-shadow(0 0 0.75rem orange);"></span> Where to buy hardware</h3>
<p class="indent">(Click to expand)</p>
</summary>
<section class="indent">
<p>All links below are affiliate links. Please also check yourself if there is a cheaper version available than the ones below. I have only linked ones that i have personally bought.</p>
<i>ESP32-2432S028R (2.8" Resistive, Cheapest)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_omjsYBJ">USB C + microB version</a></li>
<li><a href="https://s.click.aliexpress.com/e/_olKBkmz">Another USB C + microB version</a></li>
<li><a href="https://s.click.aliexpress.com/e/_oCWhgmN">microB version</a></li>
</ul>
<i>ESP32-2432S032C (3.2" Capacitive)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_okbSGmd">Only the capacitive version is supported! USB-C</a></li>
<li><a href="https://s.click.aliexpress.com/e/_oFygVwt">IPS version (not that great of a screen), Only the capacitive version is supported! USB-C</a></li>
</ul>
<i>ESP32-3248S035C (3.5" Capacitive)</i>
<ul>
<li><a href="https://s.click.aliexpress.com/e/_oCqygE9">microB version</a></li>
</ul>
</section>
</details>
</section>
<section class="issues">
<h3><span class="iconify" data-icon="mdi-github" style="color: white; filter: drop-shadow(0 0 0.75rem gray);"></span> Report Issues</h3>
<p>If you experience any issues with this project, or have any feature requests for the project, please report them on the <a href="https://github.com/suchmememanyskill/CYD-Klipper/issues">issues tab on Github</a>.</p>
</section>
<section class="install"> <section class="install">
<h3>Install</h3> <h3><span class="iconify" data-icon="mdi-download"></span> Install</h3>
<p>Note: You may need to hold the 'BOOT' button on the device while pressing install</p> <p>Select your device from the list below and click 'Connect'.<br>Note: You may need to hold the 'BOOT' button on the device while pressing install.<br><br>The 2.8" Resistive and 3.5" Capacitive models are best suited (in my opinion) for CYD-Klipper.<br><br>Note for any resistive models: You can clear touch calibration by holding the BOOT button for 8 seconds while the screen is on.</p>
<esp-web-install-button <select id="select-install-btn" onchange="setInstallButton(getElementById('select-install-btn').value)">
manifest="https://suchmememanyskill.github.io/CYD-Klipper/manifest.json"></esp-web-install-button> <option value="esp32-2432S024C-SD">ESP32-2432S024 (2.4" Capacitive)</option>
<option selected value="esp32-2432S028R">ESP32-2432S028 (2.8" Resistive)</option>
<option value="esp32-2432S032C-SD">ESP32-2432S032 (3.2" Capacitive)</option>
<option value="esp32-3248S035C">ESP32-3248S035 (3.5" Capacitive)</option>
<option value="esp32-3248S035C-V">ESP32-3248S035 (3.5" Capacitive) Vertical Orientation</option>
<option value="esp32-4827S043C-SD">ESP32-4827S043 (4.3" 480x270 Capacitive)</option>
<option value="esp32-8048S043C-SD">ESP32-8048S043 (4.3" 800x480 Capacitive)</option>
<option value="esp32-8048S043C-SD-alt">ESP32-8048S043 Alt (4.3" 800x480 Capacitive)</option>
<option value="esp32-CROWPANEL-28R">ESP32-CROWPANEL-28R (2.8" Resistive)</option>
</select>
<span id="install-btn"></span>
</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> </section>
</body> </body>

View File

@@ -1,27 +0,0 @@
{
"name": "CYD-Klipper",
"new_install_prompt_erase": true,
"builds": [
{
"chipFamily": "ESP32",
"parts": [
{
"path": "output/bootloader.bin",
"offset": 4096
},
{
"path": "output/partitions.bin",
"offset": 32768
},
{
"path": "output/boot_app0.bin",
"offset": 57344
},
{
"path": "output/firmware.bin",
"offset": 65536
}
]
}
]
}

View File

@@ -1,10 +0,0 @@
{
"name": "CYD-Klipper",
"new_install_prompt_erase": false,
"builds": [
{
"path": "output/merged-firmware.bin",
"offset": 0
}
]
}

104
ci.py Normal file
View File

@@ -0,0 +1,104 @@
import subprocess, os, shutil, json
CYD_PORTS = [
"esp32-3248S035C",
"esp32-2432S028R",
"esp32-2432S032C-SD",
"esp32-8048S043C-SD",
"esp32-8048S043C-SD-alt",
"esp32-2432S024C-SD",
"esp32-4827S043C-SD",
"esp32-3248S035C-V",
#"esp32-4827S043R-SD",
"esp32-CROWPANEL-28R",
"esp32-CROWPANEL-35C",
]
ESP_S3_CHIPS = [
"esp32-8048S043C-SD",
"esp32-8048S043C-SD-alt",
"esp32-4827S043C-SD",
"esp32-CROWPANEL-35C",
]
BASE_DIR = os.getcwd()
def get_manifest(base_path : str, device_name : str):
return {
"name": f"to {device_name}",
"funding_url": "https://ko-fi.com/suchmememanyskill",
"new_install_prompt_erase": True,
"builds": [
{
"chipFamily": "ESP32-S3" if device_name in ESP_S3_CHIPS else "ESP32",
"parts": [
{
"path": f"{base_path}/bootloader.bin",
"offset": 4096
},
{
"path": f"{base_path}/partitions.bin",
"offset": 32768
},
{
"path": f"{base_path}/boot_app0.bin",
"offset": 57344
},
{
"path": f"{base_path}/firmware.bin",
"offset": 65536
}
]
}
]
}
def extract_commit() -> str:
git_describe_output = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, text=True, check=True).stdout.strip()
return git_describe_output.split("-")[0]
repo_version = extract_commit()
configurations = []
def add_configuration(board : str):
configurations.append({
"Board": board,
"Version": repo_version,
"URL": f"https://suchmememanyskill.github.io/CYD-Klipper/out/{board}/firmware.bin"
})
if os.path.exists("out"):
shutil.rmtree("out")
for port in CYD_PORTS:
port_path = f"out/{port}"
os.chdir(BASE_DIR)
os.makedirs(port_path, exist_ok=True)
os.chdir("CYD-Klipper")
subprocess.run(["pio", "run", "-e", port], check=True)
os.chdir(BASE_DIR)
for file in ["bootloader.bin", "partitions.bin", "firmware.bin"]:
shutil.copy(f"./CYD-Klipper/.pio/build/{port}/{file}", f"{port_path}/{file}")
shutil.copy(os.path.join(os.path.expanduser("~"), ".platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin"), f"{port_path}/boot_app0.bin")
os.chdir(port_path)
if (port in ESP_S3_CHIPS):
subprocess.run(["python3", "-m", "esptool", "--chip", "esp32s3", "merge_bin", "-o", "merged_firmware.bin", "--flash_mode", "qio", "--flash_freq", "80m", "--flash_size", "16MB", "0x1000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000", "boot_app0.bin", "0x10000", "firmware.bin"], check=True)
else:
subprocess.run(["python3", "-m", "esptool", "--chip", "esp32", "merge_bin", "-o", "merged_firmware.bin", "--flash_mode", "dio", "--flash_freq", "40m", "--flash_size", "4MB", "0x1000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000", "boot_app0.bin", "0x10000", "firmware.bin"], check=True)
os.chdir(BASE_DIR)
with open(f"./_site/{port}.json", "w") as f:
json.dump(get_manifest(port_path, port), f)
add_configuration(port)
os.chdir(BASE_DIR)
out_dir = "./_site/out"
if os.path.exists(out_dir):
shutil.rmtree(out_dir)
shutil.copytree("./out", out_dir)
with open("./_site/OTA.json", "w") as f:
json.dump({"Configurations": configurations}, f)

BIN
readme/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Some files were not shown because too many files have changed in this diff Show More