Integrate support for DGUS T5UID1 touchscreens

This commit is contained in:
Desuuuu 2020-08-16 14:51:53 +02:00
parent 118ef908a5
commit cb09ec8b9e
No known key found for this signature in database
GPG Key ID: 85943F4B2C2CE0DC
20 changed files with 3297 additions and 0 deletions

View File

@ -14,3 +14,15 @@ To begin using Klipper start by
Klipper is Free Software. See the [license](COPYING) or read the Klipper is Free Software. See the [license](COPYING) or read the
[documentation](https://www.klipper3d.org/Overview.html). [documentation](https://www.klipper3d.org/Overview.html).
## Modifications
The scope of modifications is limited to adding support for DGUS
touchscreens. This feature is only available for AVR/LPC176X
micro-controllers and it needs to be configured before compilation.
The touchscreen firmware compatible with this fork is available in
[this repository](https://github.com/Desuuuu/DGUS-reloaded-Klipper).
Available configuration options are documented in the
[sample-t5uid1.cfg](/config/sample-t5uid1.cfg) file.

26
config/sample-t5uid1.cfg Normal file
View File

@ -0,0 +1,26 @@
# This file provides example configuration for DGUS T5UID1 touchscreens.
[t5uid1]
firmware: dgus_reloaded
# This controls how Klipper interacts with the touchscreen. The only possible
# value is "dgus_reloaded" at the moment. This parameter must be provided.
#update_interval: 2
# How often to send data updates to the touchscreen.
#volume: 75
# The volume for the touchscreen speaker (as a value from 0 to 100).
#brightness: 100
# The brightness for the touchscreen (as a value from 0 to 100).
#machine_name: Generic 3D Printer
# The machine name shown on the information page.
#boot_sound:
# The index of the sound to play when booting. Defaults to being provided by
# the selected firmware.
#notification_sound:
# The index of the sound to play when certain events happened. Defaults to
# being provided by the selected firmware.
#z_min:
# This can be used to provide a lower limit to Z moves. Movements will still
# be limited by your [stepper_z] configuration values.
#z_max:
# This can be used to provide an upper limit to Z moves. Movements will still
# be limited by your [stepper_z] configuration values.

View File

@ -0,0 +1,9 @@
# Package definition for the extras/t5uid1 directory
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import t5uid1
def load_config(config):
return t5uid1.load_config(config)

View File

@ -0,0 +1,110 @@
# Package definition for the extras/t5uid1/dugs_reloaded directory
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
controls = {
# print_status
'pause': 1, # popup_window
'resume': 2, # popup_window
# leveling_automatic
'disable': 5, # return_key_code
# settings_menu2
'extra2': 4, # return_key_code
# wait
'abort': 1, # popup_window
'continue': 2 # return_key_code
}
constants = {
'temp_pla': {
'hotend': 200,
'bed': 60
},
'temp_abs': {
'hotend': 240,
'bed': 80
},
'temp_petg': {
'hotend': 240,
'bed': 60
},
'popup_confirmed': 1,
'adjust_increment': 0,
'adjust_decrement': 1,
'preset_pla': 1,
'preset_abs': 2,
'preset_petg': 3,
'extruder_current': -1,
'extruder_e0': 0,
'extruder_e1': 1,
'heater_all': -2,
'heater_bed': -1,
'heater_h0': 0,
'heater_h1': 1,
'stepper_enable': 1,
'stepper_disable': 2,
'step_size_10': 0,
'step_size_1': 1,
'step_size_0.1': 2,
'step_size_0.01': 3,
'filament_retract': 0,
'filament_extrude': 1,
'axis_xyz': 0,
'axis_xy': 1,
'axis_z': 2,
'move_x+': 0,
'move_x-': 1,
'move_y+': 2,
'move_y-': 3,
'move_z+': 4,
'move_z-': 5,
'extra_button1': 0,
'extra_button2': 1,
'disabled': 0,
'enabled': 1,
'status_icon_pause': 1 << 0,
'status_icon_resume': 1 << 1,
'step_icon_10': 1 << 0,
'step_icon_1': 1 << 1,
'step_icon_0.1': 1 << 2,
'step_icon_0.01': 1 << 3,
'extruder_icon_e0': 1 << 0,
'extruder_icon_e1': 1 << 1,
'heater_icon_bed': 1 << 0,
'heater_icon_h0': 1 << 1,
'heater_icon_h1': 1 << 2,
'wait_icon_abort': 1 << 0,
'wait_icon_continue': 1 << 1
}
configuration = {
'config_files': ['routines.cfg',
'pages.cfg',
'vars_in.cfg',
'vars_out.cfg'],
'boot_sound': 1,
'notification_sound': 3,
'controls': controls,
'constants': constants
}

View File

@ -0,0 +1,112 @@
[t5uid1_page boot]
id: 0
boot: true
timeout: true
[t5uid1_page home]
id: 1
var_auto: temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target
[t5uid1_page print_status]
id: 2
var_auto:
temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target,
status_position_z, status_ellapsed, status_percent, status_icons
[t5uid1_page print_adjust]
id: 3
var_auto:
temp_h0_target, temp_bed_target, fan_speed,
adjust_feedrate, adjust_flowrate, level_offset
[t5uid1_page print_finished]
id: 4
var: status_ellapsed, status_percent
var_auto:
temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target,
status_position_z
[t5uid1_page temp_menu]
id: 5
var_auto: temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target
[t5uid1_page temp_manual]
id: 6
var: temp_h0_max, temp_bed_max
var_auto: temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target
[t5uid1_page fan]
id: 7
var_auto: fan_speed
[t5uid1_page move]
id: 8
var: move_step_icons
var_auto: move_current_x, move_current_y, move_current_z
[t5uid1_page settings_menu]
id: 9
var_auto: stepper_status
[t5uid1_page leveling_menu]
id: 10
[t5uid1_page leveling_offset]
id: 11
var: level_offset_step_icons
var_auto: level_offset
[t5uid1_page leveling_manual]
id: 12
var_auto: temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target
[t5uid1_page leveling_automatic]
id: 13
var: level_auto_disable_icon, level_auto_grid
var_auto:
temp_h0_current, temp_h0_target, temp_bed_current, temp_bed_target
[t5uid1_page leveling_probing]
id: 14
var_auto: level_probing_icons
[t5uid1_page filament]
id: 15
var: filament_icons, filament_length
var_auto: temp_h0_current, temp_h0_target
[t5uid1_page volume]
id: 16
var: volume
[t5uid1_page brightness]
id: 17
var: brightness
[t5uid1_page settings_menu2]
id: 18
var: bltouch
[t5uid1_page pid]
id: 19
var: pid_heater_icons, pid_temp
var_auto: pid_kp, pid_ki, pid_kd
[t5uid1_page info]
id: 20
var: info_machine, info_build_volume, info_version
[t5uid1_page debug1]
id: 240
[t5uid1_page debug2]
id: 241
[t5uid1_page wait]
id: 249
var: line1, line2, line3, line4, wait_icons
[t5uid1_page kill]
id: 250
shutdown: true
var: line1, line2, line3, line4

View File

@ -0,0 +1,207 @@
[t5uid1_routine message_timeout]
trigger: manual
delay: 30
script: {% do set_message("") %}
[t5uid1_routine print_start]
trigger: manual
delay: 0
script: {% do switch_page("print_status") %}
[t5uid1_routine print_end]
trigger: manual
delay: 0
script:
{% set sound = printer.t5uid1.notification_sound %}
{% if sound >= 0 %}
{% do play_sound(sound) %}
{% endif %}
{% do switch_page("print_finished") %}
[t5uid1_routine trigger_full_update]
trigger: manual
delay: 0
script: {% do full_update() %}
[t5uid1_routine show_wait_screen]
trigger: manual
delay: 0
script: {% do switch_page("wait") %}
[t5uid1_routine show_probing_screen]
trigger: manual
delay: 0
script: {% do switch_page("leveling_probing") %}
[t5uid1_routine reset_bltouch]
trigger: manual
script:
{% if printer.t5uid1.has_bltouch %}
BLTOUCH_DEBUG COMMAND=reset
BLTOUCH_DEBUG COMMAND=pin_up
{% endif %}
run_as_gcode: true
[t5uid1_routine __wait_return]
trigger: enter
page: wait
interval: 1
script:
{% if not is_busy() %}
{% set sound = get_variable("wait_return_sound", -1) %}
{% if sound >= 0 and sound <= 255 %}
{% do set_variable("wait_return_sound", -1) %}
{% do play_sound(sound) %}
{% endif %}
{% set page = get_variable("wait_return", "")|default("home", true) %}
{% do set_variable("wait_return", "") %}
{% do switch_page(page) %}
{% endif %}
[t5uid1_routine __probing_return]
trigger: enter
page: leveling_probing
interval: 1
script:
{% if not is_busy() %}
{% set sound = printer.t5uid1.notification_sound %}
{% if sound >= 0 %}
{% do play_sound(sound) %}
{% endif %}
{% do switch_page("leveling_automatic") %}
{% endif %}
[t5uid1_routine __boot]
trigger: enter
page: boot
delay: 3
script: {% do switch_page("home") %}
[t5uid1_routine __print_status_pause]
trigger: enter
page: print_status
interval: 5
script: {% do check_paused() %}
[t5uid1_routine __print_adjust_pause]
trigger: enter
page: print_adjust
interval: 5
script: {% do check_paused() %}
[t5uid1_routine __move]
trigger: enter_pre
page: move
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{ abort_page_switch() }
{% else %}
{% do set_variable("move_steps", printer.t5uid1.constants.step_size_10) %}
{% endif %}
[t5uid1_routine __leveling_menu]
trigger: enter_pre
page: leveling_menu
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{ abort_page_switch() }
{% elif printer.toolhead.homed_axes == "xyz" %}
{% set t5uid1 = printer.t5uid1 %}
{% set x = (t5uid1.limits.x_min + t5uid1.limits.x_max) / 2 %}
{% set y = (t5uid1.limits.y_min + t5uid1.limits.y_max) / 2 %}
{% if printer.gcode.move_zpos < 10 %}
G1 Z10 F600
{% endif %}
G1 X{x} Y{y} F4800
{% else %}
{ abort_page_switch() }
{% do set_variable("line1", "") %}
{% do set_variable("line2", "Homing...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", "leveling_menu") %}
G28
{% do start_routine("show_wait_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_routine __leveling_offset]
trigger: enter_pre
page: leveling_offset
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{ abort_page_switch() }
{% elif printer.toolhead.homed_axes == "xyz" %}
{% set t5uid1 = printer.t5uid1 %}
{% set x = (t5uid1.limits.x_min + t5uid1.limits.x_max) / 2 %}
{% set y = (t5uid1.limits.y_min + t5uid1.limits.y_max) / 2 %}
{% if printer.gcode.move_zpos < 5 %}
G1 Z5 F600
{% endif %}
G1 X{x} Y{y} F4800
G1 Z0 F300
{% else %}
{ abort_page_switch() }
{% do set_variable("line1", "") %}
{% do set_variable("line2", "Homing...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", "leveling_offset") %}
G28
{% do start_routine("show_wait_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_routine __leveling_manual]
trigger: enter_pre
page: leveling_manual
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{ abort_page_switch() }
{% elif printer.toolhead.homed_axes != "xyz" %}
{ abort_page_switch() }
{% do set_variable("line1", "") %}
{% do set_variable("line2", "Homing...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", "leveling_manual") %}
G28
{% do start_routine("show_wait_screen") %}
{% else %}
BED_MESH_PROFILE SAVE=t5uid1_mesh_backup
BED_MESH_CLEAR
{% endif %}
run_as_gcode: true
[t5uid1_routine __leveling_manual_leave]
trigger: leave
page: leveling_manual
script:
BED_MESH_PROFILE LOAD=t5uid1_mesh_backup
BED_MESH_PROFILE REMOVE=t5uid1_mesh_backup
run_as_gcode: true
[t5uid1_routine __filament]
trigger: enter_pre
page: filament
script:
{% set t5uid1 = printer.t5uid1 %}
{% do set_variable("filament_extruder", t5uid1.constants.extruder_current) %}
{% do set_variable("filament_length", 10) %}
[t5uid1_routine __pid]
trigger: enter_pre
page: pid
script:
{% set t5uid1 = printer.t5uid1 %}
{% do set_variable("pid_heater", t5uid1.constants.heater_h0) %}
{% do set_variable("pid_temp", t5uid1.constants.temp_pla.hotend) %}
[t5uid1_routine __info]
trigger: enter_pre
page: info
script: {% do set_variable("debug_count", 0) %}

View File

@ -0,0 +1,706 @@
[t5uid1_var __switch_page]
type: input
address: 0x2000
data_type: uint16
script: {% do switch_page(page_name(data)) %}
[t5uid1_var __switch_page_idle]
type: input
address: 0x2002
data_type: uint16
script:
{% if not is_busy() %}
{% do switch_page(page_name(data)) %}
{% endif %}
[t5uid1_var __switch_page_printing]
type: input
address: 0x2003
data_type: uint16
script:
{% if printer.t5uid1.is_printing %}
{% do switch_page(page_name(data)) %}
{% endif %}
[t5uid1_var __status_abort]
type: input
address: 0x2007
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.popup_confirmed %}
{% if t5uid1.is_printing %}
RESPOND TYPE=command MSG=action:cancel
{% endif %}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __status_pause]
type: input
address: 0x2008
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.popup_confirmed %}
{% if t5uid1.is_printing and not printer.pause_resume.is_paused %}
RESPOND TYPE=command MSG=action:pause
{% endif %}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __status_resume]
type: input
address: 0x2009
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.popup_confirmed %}
{% if t5uid1.is_printing and printer.pause_resume.is_paused %}
RESPOND TYPE=command MSG=action:resume
{% endif %}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __adjust_set_feedrate]
type: input
address: 0x200a
data_type: int16
script:
M220 S{data}
{% do start_routine("trigger_full_update") %}
run_as_gcode: true
[t5uid1_var __adjust_set_flowrate]
type: input
address: 0x200b
data_type: int16
script:
M221 S{data}
{% do start_routine("trigger_full_update") %}
run_as_gcode: true
[t5uid1_var __adjust_set_offset]
type: input
address: 0x200e
data_type: int16
script:
{% if "z" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set offset = data|float / 10 ** 2 %}
SET_GCODE_OFFSET Z={offset}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __adjust_offset]
type: input
address: 0x200f
data_type: uint16
script:
{% if "z" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.adjust_increment %}
SET_GCODE_OFFSET Z_ADJUST=0.01
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.adjust_decrement %}
SET_GCODE_OFFSET Z_ADJUST=-0.01
{% do start_routine("trigger_full_update") %}
{% endif %}
{% endif %}
run_as_gcode: true
[t5uid1_var __temp_preset]
type: input
address: 0x2010
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.preset_pla %}
SET_HEATER_TEMPERATURE HEATER={printer.toolhead.extruder} TARGET={t5uid1.constants.temp_pla.hotend}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={t5uid1.constants.temp_pla.bed}
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.preset_abs %}
SET_HEATER_TEMPERATURE HEATER={printer.toolhead.extruder} TARGET={t5uid1.constants.temp_abs.hotend}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={t5uid1.constants.temp_abs.bed}
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.preset_petg %}
SET_HEATER_TEMPERATURE HEATER={printer.toolhead.extruder} TARGET={t5uid1.constants.temp_petg.hotend}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={t5uid1.constants.temp_petg.bed}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __temp_set_target_bed]
type: input
address: 0x2011
data_type: int16
script:
{% set max_temp = heater_max_temp("heater_bed", 10)|round|int %}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={[data, max_temp]|min}
{% do start_routine("trigger_full_update") %}
run_as_gcode: true
[t5uid1_var __temp_set_target_h0]
type: input
address: 0x2012
data_type: int16
script:
{% set max_temp = heater_max_temp("extruder", 15)|round|int %}
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={[data, max_temp]|min}
{% do start_routine("trigger_full_update") %}
run_as_gcode: true
[t5uid1_var __temp_set_target_h1]
type: input
address: 0x2013
data_type: int16
script:
{% if 'extruder1' in printer %}
{% set max_temp = heater_max_temp("extruder1", 15)|round|int %}
SET_HEATER_TEMPERATURE HEATER=extruder1 TARGET={[data, max_temp]|min}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __temp_cool]
type: input
address: 0x2014
data_type: int16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.heater_all %}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=0
SET_HEATER_TEMPERATURE HEATER=extruder TARGET=0
{% if 'extruder1' in printer %}
SET_HEATER_TEMPERATURE HEATER=extruder1 TARGET=0
{% endif %}
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.heater_bed %}
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=0
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.heater_h0 %}
SET_HEATER_TEMPERATURE HEATER=extruder TARGET=0
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.heater_h1 %}
{% if 'extruder1' in printer %}
SET_HEATER_TEMPERATURE HEATER=extruder1 TARGET=0
{% do start_routine("trigger_full_update") %}
{% endif %}
{% endif %}
run_as_gcode: true
[t5uid1_var __stepper_control]
type: input
address: 0x2015
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.stepper_enable %}
{% set steppers = ['stepper_x', 'stepper_y', 'stepper_z', 'extruder'] %}
{% if 'extruder1' in printer %}
{% do steppers.append('extruder1') %}
{% endif %}
{% for stepper in steppers %}
SET_STEPPER_ENABLE STEPPER={stepper} ENABLE=1
{% endfor %}
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.stepper_disable %}
M18
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __level_offset_set]
type: input
address: 0x2016
data_type: int16
script:
{% if "z" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set offset = data|float / 10 ** 2 %}
SET_GCODE_OFFSET Z={offset}
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __level_offset_step]
type: input
address: 0x2017
data_type: uint16
script:
{% if "z" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set offset_steps = get_variable("offset_steps",
t5uid1.constants['step_size_0.1']) %}
{% if offset_steps == t5uid1.constants['step_size_0.1'] %}
{% set step = "0.1" %}
{% elif offset_steps == t5uid1.constants['step_size_0.01'] %}
{% set step = "0.01" %}
{% endif %}
{% if data == t5uid1.constants.adjust_increment %}
SET_GCODE_OFFSET Z_ADJUST={step}
{% do start_routine("trigger_full_update") %}
{% elif data == t5uid1.constants.adjust_decrement %}
SET_GCODE_OFFSET Z_ADJUST=-{step}
{% do start_routine("trigger_full_update") %}
{% endif %}
{% endif %}
run_as_gcode: true
[t5uid1_var __level_offset_set_step]
type: input
address: 0x2018
data_type: uint16
script:
{% do set_variable("offset_steps", data) %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __level_manual_point]
type: input
address: 0x2019
data_type: uint16
script:
{% if printer.toolhead.homed_axes != "xyz" %}
{% do set_message("Homing required") %}
{% elif is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% set inset = 30 %}
{% set hop = 5 %}
{% set t5uid1 = printer.t5uid1 %}
{% if data == 1 %}
{% set x = (t5uid1.limits.x_max - t5uid1.limits.x_min) / 2 %}
{% set y = (t5uid1.limits.y_max - t5uid1.limits.y_min) / 2 %}
{% elif data == 2 %}
{% set x = t5uid1.limits.x_min + inset %}
{% set y = t5uid1.limits.y_min + inset %}
{% elif data == 3 %}
{% set x = t5uid1.limits.x_max - inset %}
{% set y = t5uid1.limits.y_min + inset %}
{% elif data == 4 %}
{% set x = t5uid1.limits.x_max - inset %}
{% set y = t5uid1.limits.y_max - inset %}
{% elif data == 5 %}
{% set x = t5uid1.limits.x_min + inset %}
{% set y = t5uid1.limits.y_max - inset %}
{% endif %}
SAVE_GCODE_STATE NAME=state_level_manual_point
G90
{% if printer.gcode.move_zpos < hop %}
G1 Z{hop} F600
{% endif %}
G1 X{x} Y{y} F4800
G1 Z0 F300
RESTORE_GCODE_STATE NAME=state_level_manual_point
{% endif %}
run_as_gcode: true
[t5uid1_var __level_auto_probe]
type: input
address: 0x201a
data_type: none
script:
{% if printer.toolhead.homed_axes != "xyz" %}
{% do set_message("Homing required") %}
{% elif is_busy() %}
{% do set_message("Busy") %}
{% else %}
BED_MESH_CALIBRATE
G28 Z
{% do start_routine("show_probing_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __level_auto_disable]
type: input
address: 0x201b
data_type: none
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
BED_MESH_CLEAR
BED_MESH_PROFILE REMOVE=default
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __filament_select]
type: input
address: 0x201c
data_type: int16
script:
{% do set_variable("filament_extruder", data) %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __filament_set_length]
type: input
address: 0x201d
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set extruder = get_variable("filament_extruder",
t5uid1.constants.extruder_current) %}
{% if extruder == t5uid1.constants.extruder_current %}
{% set len = limit_extrude(printer.toolhead.extruder, data) %}
{% elif extruder == t5uid1.constants.extruder_e0 %}
{% set len = limit_extrude("extruder", data) %}
{% elif extruder == t5uid1.constants.extruder_e1 %}
{% set len = limit_extrude("extruder1", data) %}
{% endif %}
{% do set_variable("filament_length", len|round|int) %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __filament_move]
type: input
address: 0x201e
data_type: uint16
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set extruder = get_variable("filament_extruder",
t5uid1.constants.extruder_current) %}
{% if extruder == t5uid1.constants.extruder_current %}
{% set ext_name = printer.toolhead.extruder %}
{% elif extruder == t5uid1.constants.extruder_e0 %}
{% set ext_name = "extruder" %}
{% elif extruder == t5uid1.constants.extruder_e1 %}
{% set ext_name = "extruder1" %}
{% endif %}
{% if ext_name not in printer %}
{% do set_message("Invalid extruder") %}
{% elif printer[ext_name].temperature < heater_min_extrude_temp(ext_name) %}
{% do set_message("Temperature too low") %}
{% else %}
SAVE_GCODE_STATE NAME=state_filament_move
ACTIVATE_EXTRUDER EXTRUDER={ext_name}
M83
{% set length = get_variable("filament_length", 10) %}
{% if data == t5uid1.constants.filament_retract %}
G1 E-{length}
{% elif data == t5uid1.constants.filament_extrude %}
G1 E{length}
{% endif %}
RESTORE_GCODE_STATE NAME=state_filament_move
{% endif %}
{% endif %}
run_as_gcode: true
[t5uid1_var __home]
type: input
address: 0x201f
data_type: uint16
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% do set_variable("line1", "") %}
{% do set_variable("line2", "Homing...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", t5uid1.page) %}
{% if data == t5uid1.constants.axis_xyz %}
G28 X Y Z
{% elif data == t5uid1.constants.axis_xy %}
G28 X Y
{% elif data == t5uid1.constants.axis_z %}
G28 Z
{% endif %}
{% do start_routine("show_wait_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __move_x]
type: input
address: 0x2020
data_type: int16
script:
{% if "x" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set pos = data|float / 10 ** 1 %}
{% if pos < t5uid1.limits.x_min %}
{% set pos = t5uid1.limits.x_min %}
{% elif pos > t5uid1.limits.x_max %}
{% set pos = t5uid1.limits.x_max %}
{% endif %}
SAVE_GCODE_STATE NAME=state_move_x
G90
G1 X{pos} F4800
RESTORE_GCODE_STATE NAME=state_move_x
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __move_y]
type: input
address: 0x2021
data_type: int16
script:
{% if "y" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set pos = data|float / 10 ** 1 %}
{% if pos < t5uid1.limits.y_min %}
{% set pos = t5uid1.limits.y_min %}
{% elif pos > t5uid1.limits.y_max %}
{% set pos = t5uid1.limits.y_max %}
{% endif %}
SAVE_GCODE_STATE NAME=state_move_y
G90
G1 Y{pos} F4800
RESTORE_GCODE_STATE NAME=state_move_y
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __move_z]
type: input
address: 0x2022
data_type: int16
script:
{% if "z" not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set pos = data|float / 10 ** 1 %}
{% if pos < t5uid1.limits.z_min %}
{% set pos = t5uid1.limits.z_min %}
{% elif pos > t5uid1.limits.z_max %}
{% set pos = t5uid1.limits.z_max %}
{% endif %}
SAVE_GCODE_STATE NAME=state_move_z
G90
G1 Z{pos} F900
RESTORE_GCODE_STATE NAME=state_move_z
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __move_step]
type: input
address: 0x2023
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set move_steps = get_variable("move_steps",
t5uid1.constants.step_size_10) %}
{% if move_steps == t5uid1.constants.step_size_10 %}
{% set step = 10.0 %}
{% elif move_steps == t5uid1.constants.step_size_1 %}
{% set step = 1.0 %}
{% elif move_steps == t5uid1.constants['step_size_0.1'] %}
{% set step = 0.1 %}
{% endif %}
{% if data == t5uid1.constants['move_x+'] %}
{% set axis = "x" %}
{% set pos = printer.gcode.move_xpos + step %}
{% set speed = 4800 %}
{% elif data == t5uid1.constants['move_x-'] %}
{% set axis = "x" %}
{% set pos = printer.gcode.move_xpos - step %}
{% set speed = 4800 %}
{% elif data == t5uid1.constants['move_y+'] %}
{% set axis = "y" %}
{% set pos = printer.gcode.move_ypos + step %}
{% set speed = 4800 %}
{% elif data == t5uid1.constants['move_y-'] %}
{% set axis = "y" %}
{% set pos = printer.gcode.move_ypos - step %}
{% set speed = 4800 %}
{% elif data == t5uid1.constants['move_z+'] %}
{% set axis = "z" %}
{% set pos = printer.gcode.move_zpos + step %}
{% set speed = 900 %}
{% elif data == t5uid1.constants['move_z-'] %}
{% set axis = "z" %}
{% set pos = printer.gcode.move_zpos - step %}
{% set speed = 900 %}
{% endif %}
{% if axis not in printer.toolhead.homed_axes %}
{% do set_message("Homing required") %}
{% else %}
{% if pos < t5uid1.limits[axis ~ "_min"] %}
{% set pos = t5uid1.limits[axis ~ "_min"] %}
{% elif pos > t5uid1.limits[axis ~ "_max"] %}
{% set pos = t5uid1.limits[axis ~ "_max"] %}
{% endif %}
SAVE_GCODE_STATE NAME=state_move_step
G90
G1 {axis|upper}{pos} F{speed}
RESTORE_GCODE_STATE NAME=state_move_step
{% do start_routine("trigger_full_update") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __move_set_step]
type: input
address: 0x2024
data_type: uint16
script:
{% do set_variable("move_steps", data) %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __settings2_extra]
type: input
address: 0x2028
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.extra_button1 %}
{% if t5uid1.has_bltouch %}
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% do start_routine("reset_bltouch") %}
{% endif %}
{% else %}
{% do switch_page("info") %}
{% endif %}
{% elif data == t5uid1.constants.extra_button2 %}
{% if t5uid1.has_bltouch %}
{% do switch_page("info") %}
{% endif %}
{% endif %}
[t5uid1_var __pid_select]
type: input
address: 0x2029
data_type: int16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if data == t5uid1.constants.heater_bed %}
{% do set_variable("pid_temp", t5uid1.constants.temp_pla.bed) %}
{% do set_variable("pid_heater", data) %}
{% elif data == t5uid1.constants.heater_h0 %}
{% do set_variable("pid_temp", t5uid1.constants.temp_pla.hotend) %}
{% do set_variable("pid_heater", data) %}
{% elif data == t5uid1.constants.heater_h1 %}
{% do set_variable("pid_temp", t5uid1.constants.temp_pla.hotend) %}
{% do set_variable("pid_heater", data) %}
{% endif %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __pid_set_temp]
type: input
address: 0x202a
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
{% set min_temp = heater_min_temp("heater_bed")|round|int %}
{% set max_temp = heater_max_temp("heater_bed", 10)|round|int %}
{% elif pid_heater == t5uid1.constants.heater_h0 %}
{% set min_temp = heater_min_temp("extruder")|round|int %}
{% set max_temp = heater_max_temp("extruder", 15)|round|int %}
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{% set min_temp = heater_min_temp("extruder1")|round|int %}
{% set max_temp = heater_max_temp("extruder1", 15)|round|int %}
{% endif %}
{% do set_variable("pid_temp", [([data, max_temp]|min), min_temp]|max) %}
{% do start_routine("trigger_full_update") %}
[t5uid1_var __pid_run]
type: input
address: 0x202b
data_type: none
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% set pid_temp = get_variable("pid_temp",
t5uid1.constants.temp_pla.hotend) %}
{% do set_variable("line1", "") %}
{% do set_variable("line2", "PID autotuning...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", "pid") %}
{% do set_variable("wait_return_sound", t5uid1.notification_sound) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
PID_CALIBRATE HEATER=heater_bed TARGET={pid_temp}
{% elif pid_heater == t5uid1.constants.heater_h0 %}
PID_CALIBRATE HEATER=extruder TARGET={pid_temp}
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{% if 'extruder1' in printer %}
PID_CALIBRATE HEATER=extruder1 TARGET={pid_temp}
{% endif %}
{% endif %}
{% do start_routine("show_wait_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __save_config]
type: input
address: 0x2030
data_type: none
script:
{% if is_busy() %}
{% do set_message("Busy") %}
{% else %}
{% do set_variable("line1", "") %}
{% do set_variable("line2", "Saving configuration...") %}
{% do set_variable("line3", "") %}
{% do set_variable("line4", "") %}
{% do set_variable("wait_return", "boot") %}
SAVE_CONFIG
{% do start_routine("show_wait_screen") %}
{% endif %}
run_as_gcode: true
[t5uid1_var __fan_speed]
type: input
address: 0x4000
data_type: uint16
script:
{% if data >= 0 and data <= 100 %}
M106 S{(data|float / 100 * 255)|round|int}
{% endif %}
run_as_gcode: true
[t5uid1_var __volume]
type: input
address: 0x4022
data_type: uint16
script:
{% if data >= 0 and data <= 100 %}
{% do set_volume(data) %}
{% endif %}
[t5uid1_var __brightness]
type: input
address: 0x4023
data_type: uint16
script:
{% if data >= 0 and data <= 100 %}
{% do set_brightness(data) %}
{% endif %}
[t5uid1_var __debug]
type: input
address: 0x5001
data_type: none
script:
{% set counter = get_variable("debug_count", 0)|int + 1 %}
{% do set_variable("debug_count", counter) %}
{% if counter >= 10 %}
{% do switch_page("debug1") %}
{% endif %}

View File

@ -0,0 +1,461 @@
[t5uid1_var line1]
type: output
address: 0x1100
data_type: str
data_len: 32
script: { "{:^32s}".format(get_variable("line1", "")|trim) }
[t5uid1_var line2]
type: output
address: 0x1120
data_type: str
data_len: 32
script: { "{:^32s}".format(get_variable("line2", "")|trim) }
[t5uid1_var line3]
type: output
address: 0x1140
data_type: str
data_len: 32
script: { "{:^32s}".format(get_variable("line3", "")|trim) }
[t5uid1_var line4]
type: output
address: 0x1160
data_type: str
data_len: 32
script: { "{:^32s}".format(get_variable("line4", "")|trim) }
[t5uid1_var message]
type: output
address: 0x3000
data_type: str
data_len: 32
script: { "{:>32s}".format(get_variable("message", "")|trim) }
[t5uid1_var status_position_z]
type: output
address: 0x30e6
data_type: int16
script: { (printer.gcode.move_zpos * 10 ** 1)|round|int }
[t5uid1_var status_ellapsed]
type: output
address: 0x30e7
data_type: str
data_len: 15
script: { get_duration(printer.t5uid1.print_duration) }
[t5uid1_var status_percent]
type: output
address: 0x30f6
data_type: uint16
script: { printer.t5uid1.print_progress }
[t5uid1_var status_icons]
type: output
address: 0x30f7
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if not t5uid1.is_printing %}
{% do disable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.pause) %}
{% do disable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.resume) %}
{ "0" }
{% elif printer.pause_resume.is_paused %}
{% do enable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.resume) %}
{% do disable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.pause) %}
{ t5uid1.constants.status_icon_resume }
{% else %}
{% do enable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.pause) %}
{% do disable_control(t5uid1.pages.print_status,
t5uid1.control_types.popup_window,
t5uid1.controls.resume) %}
{ t5uid1.constants.status_icon_pause }
{% endif %}
[t5uid1_var adjust_feedrate]
type: output
address: 0x30f8
data_type: int16
script: { (printer.gcode.speed_factor * 100)|round|int }
[t5uid1_var adjust_flowrate]
type: output
address: 0x30f9
data_type: int16
script: { (printer.gcode.extrude_factor * 100)|round|int }
[t5uid1_var temp_bed_current]
type: output
address: 0x30fc
data_type: int16
script: { printer.heater_bed.temperature|round|int }
[t5uid1_var temp_bed_target]
type: output
address: 0x30fd
data_type: int16
script: { printer.heater_bed.target|round|int }
[t5uid1_var temp_bed_max]
type: output
address: 0x30fe
data_type: uint16
script: { heater_max_temp("heater_bed", 10)|round|int }
[t5uid1_var temp_h0_current]
type: output
address: 0x30ff
data_type: int16
script: { printer.extruder.temperature|round|int }
[t5uid1_var temp_h0_target]
type: output
address: 0x3100
data_type: int16
script: { printer.extruder.target|round|int }
[t5uid1_var temp_h0_max]
type: output
address: 0x3101
data_type: uint16
script: { heater_max_temp("extruder", 15)|round|int }
[t5uid1_var temp_h1_current]
type: output
address: 0x3102
data_type: int16
script:
{% if 'extruder1' in printer %}
{ printer.extruder1.temperature|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var temp_h1_target]
type: output
address: 0x3103
data_type: int16
script:
{% if 'extruder1' in printer %}
{ printer.extruder1.target|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var temp_h1_max]
type: output
address: 0x3104
data_type: uint16
script:
{% if 'extruder1' in printer %}
{ heater_max_temp("extruder1", 15)|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var stepper_status]
type: output
address: 0x3105
data_type: uint16
script:
{% if all_steppers_enabled() %}
{ printer.t5uid1.constants.enabled }
{% else %}
{ printer.t5uid1.constants.disabled }
{% endif %}
[t5uid1_var level_offset]
type: output
address: 0x3106
data_type: int16
script: { (printer.gcode.base_zpos * 10 ** 1)|round|int }
[t5uid1_var level_offset_step_icons]
type: output
address: 0x3107
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set offset_steps = get_variable("offset_steps",
t5uid1.constants['step_size_0.1']) %}
{% if offset_steps == t5uid1.constants['step_size_0.1'] %}
{ t5uid1.constants['step_icon_0.1'] }
{% elif offset_steps == t5uid1.constants['step_size_0.01'] %}
{ t5uid1.constants['step_icon_0.01'] }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var level_auto_disable_icon]
type: output
address: 0x3108
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if printer.bed_mesh.profile_name != "" %}
{% do enable_control(t5uid1.pages.leveling_automatic,
t5uid1.control_types.return_key_code,
t5uid1.controls.disable) %}
{ t5uid1.constants.enabled }
{% else %}
{% do disable_control(t5uid1.pages.leveling_automatic,
t5uid1.control_types.return_key_code,
t5uid1.controls.disable) %}
{ t5uid1.constants.disabled }
{% endif %}
[t5uid1_var level_auto_grid]
type: output
address: 0x3109
data_type: array[int16]
array_len: 25
script:
{% set grid = printer.bed_mesh.probed_matrix %}
{% if not grid or grid|length < 5 %}
{% set grid = [] %}
{% for i in range(5) %}
{% do grid.append([0, 0, 0, 0, 0]) %}
{% endfor %}
{% endif %}
{% for line in grid %}
{% for val in line %}
{ (val * 10 ** 3)|round|int },
{% endfor %}
{% endfor %}
[t5uid1_var level_probing_icons]
type: output
address: 0x3122
data_type: uint32
script: { probed_matrix() }
[t5uid1_var filament_icons]
type: output
address: 0x3124
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set extruder = get_variable("filament_extruder",
t5uid1.constants.extruder_current) %}
{% if extruder == t5uid1.constants.extruder_current %}
{% if printer.toolhead.extruder == "extruder" %}
{ t5uid1.constants.extruder_icon_e0 }
{% elif printer.toolhead.extruder == "extruder1" %}
{ t5uid1.constants.extruder_icon_e1 }
{% else %}
{ "0" }
{% endif %}
{% elif extruder == t5uid1.constants.extruder_e0 %}
{ t5uid1.constants.extruder_icon_e0 }
{% elif extruder == t5uid1.constants.extruder_e1 %}
{ t5uid1.constants.extruder_icon_e1 }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var filament_length]
type: output
address: 0x3125
data_type: uint16
script: { get_variable("filament_length", 10) }
[t5uid1_var move_current_x]
type: output
address: 0x3126
data_type: int16
script: { (printer.gcode.move_xpos * 10 ** 1)|round|int }
[t5uid1_var move_current_y]
type: output
address: 0x3127
data_type: int16
script: { (printer.gcode.move_ypos * 10 ** 1)|round|int }
[t5uid1_var move_current_z]
type: output
address: 0x3128
data_type: int16
script: { (printer.gcode.move_zpos * 10 ** 1)|round|int }
[t5uid1_var move_step_icons]
type: output
address: 0x3129
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set move_steps = get_variable("move_steps",
t5uid1.constants.step_size_10) %}
{% if move_steps == t5uid1.constants.step_size_10 %}
{ t5uid1.constants.step_icon_10 }
{% elif move_steps == t5uid1.constants.step_size_1 %}
{ t5uid1.constants.step_icon_1 }
{% elif move_steps == t5uid1.constants['step_size_0.1'] %}
{ t5uid1.constants['step_icon_0.1'] }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var bltouch]
type: output
address: 0x312a
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% if t5uid1.has_bltouch %}
{% do enable_control(t5uid1.pages.settings_menu2,
t5uid1.control_types.return_key_code,
t5uid1.controls.extra2) %}
{ t5uid1.constants.enabled }
{% else %}
{% do disable_control(t5uid1.pages.settings_menu2,
t5uid1.control_types.return_key_code,
t5uid1.controls.extra2) %}
{ t5uid1.constants.disabled }
{% endif %}
[t5uid1_var pid_heater_icons]
type: output
address: 0x312b
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
{ t5uid1.constants.heater_icon_bed }
{% elif pid_heater == t5uid1.constants.heater_h0 %}
{ t5uid1.constants.heater_icon_h0 }
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{ t5uid1.constants.heater_icon_h1 }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var pid_temp]
type: output
address: 0x312c
data_type: uint16
script: { get_variable("pid_temp", printer.t5uid1.constants.temp_pla.hotend) }
[t5uid1_var pid_kp]
type: output
address: 0x312d
data_type: int32
script:
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
{ (pid_param("heater_bed", "p") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h0 %}
{ (pid_param("extruder", "p") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{ (pid_param("extruder1", "p") * 10 ** 2)|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var pid_ki]
type: output
address: 0x312f
data_type: int32
script:
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
{ (pid_param("heater_bed", "i") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h0 %}
{ (pid_param("extruder", "i") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{ (pid_param("extruder1", "i") * 10 ** 2)|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var pid_kd]
type: output
address: 0x3131
data_type: int32
script:
{% set t5uid1 = printer.t5uid1 %}
{% set pid_heater = get_variable("pid_heater",
t5uid1.constants.heater_h0) %}
{% if pid_heater == t5uid1.constants.heater_bed %}
{ (pid_param("heater_bed", "d") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h0 %}
{ (pid_param("extruder", "d") * 10 ** 2)|round|int }
{% elif pid_heater == t5uid1.constants.heater_h1 %}
{ (pid_param("extruder1", "d") * 10 ** 2)|round|int }
{% else %}
{ "0" }
{% endif %}
[t5uid1_var info_machine]
type: output
address: 0x3133
data_type: str
data_len: 24
script: { printer.t5uid1.machine_name }
[t5uid1_var info_build_volume]
type: output
address: 0x314b
data_type: str
data_len: 24
script:
{% set limits = printer.t5uid1.limits %}
{ "{:d}x{:d}x{:d}".format((limits.x_max - limits.x_min)|round(0, 'floor')|int,
(limits.y_max - limits.y_min)|round(0, 'floor')|int,
(limits.z_max - limits.z_min)|round(0, 'floor')|int) }
[t5uid1_var info_version]
type: output
address: 0x3163
data_type: str
data_len: 16
script: { printer.t5uid1.version }
[t5uid1_var wait_icons]
type: output
address: 0x31bd
data_type: uint16
script:
{% set t5uid1 = printer.t5uid1 %}
{% do disable_control(t5uid1.pages.wait,
t5uid1.control_types.popup_window,
t5uid1.controls.abort) %}
{% do disable_control(t5uid1.pages.wait,
t5uid1.control_types.return_key_code,
t5uid1.controls.continue) %}
{ "0" }
[t5uid1_var fan_speed]
type: output
address: 0x4000
data_type: uint16
script: { (printer.fan.speed * 100)|round|int }
[t5uid1_var volume]
type: output
address: 0x4022
data_type: uint16
script: { printer.t5uid1.volume }
[t5uid1_var brightness]
type: output
address: 0x4023
data_type: uint16
script: { printer.t5uid1.brightness }

View File

@ -0,0 +1,38 @@
# Page class
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
class T5UID1_Page:
def __init__(self, var_names, config):
self.printer = config.get_printer()
name_parts = config.get_name().split()
if len(name_parts) != 2:
raise config.error("Section name '%s' is not valid"
% (config.get_name(),))
self.name = name_parts[1]
self.id = config.getint('id', minval=0, maxval=255)
self.is_boot = config.getboolean('boot', False)
self.is_timeout = config.getboolean('timeout', False)
self.is_shutdown = config.getboolean('shutdown', False)
self.var_auto = []
self.var = []
for var in config.get('var_auto', '').split(','):
var = var.strip()
if len(var) > 0 and var not in self.var_auto:
if var not in var_names:
raise config.error("Invalid var '%s' in section '%s'"
% (var, config.get_name()))
self.var_auto.append(var)
for var in config.get('var', '').split(','):
var = var.strip()
if (len(var) > 0
and var not in self.var_auto and var not in self.var):
if var not in var_names:
raise config.error("Invalid var '%s' in section '%s'"
% (var, config.get_name()))
self.var.append(var)

View File

@ -0,0 +1,102 @@
# Routine class
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
TRIGGERS = [
"enter_pre",
"enter",
"leave",
"manual"
]
class T5UID1_Routine:
def __init__(self, gcode_macro, context, page_names, config):
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
self.reactor = self.printer.get_reactor()
name_parts = config.get_name().split()
if len(name_parts) != 2:
raise config.error("Section name '%s' is not valid"
% (config.get_name(),))
self.name = name_parts[1]
self.trigger = config.get('trigger')
if self.trigger not in TRIGGERS:
raise config.error("Invalid trigger '%s' in section '%s'"
% (self.trigger, config.get_name()))
if self.trigger != "manual":
self.page = config.get('page')
if self.page not in page_names:
raise config.error("Invalid page '%s' in section '%s'"
% (self.page, config.get_name()))
else:
self.page = None
if self.trigger != "enter_pre":
self.delay = config.getint('delay', None, minval=0, maxval=60)
else:
self.delay = None
if self.trigger in ["enter", "manual"]:
self.interval = config.getint('interval', 0, minval=0, maxval=60)
else:
self.interval = 0
self._template = gcode_macro.load_template(config, 'script')
self._context = context
self.run_as_gcode = config.getboolean('run_as_gcode', False)
self._should_stop = False
self._timer = self.reactor.register_timer(self._timer_run)
def _timer_run(self, eventtime):
if self._should_stop:
return self.reactor.NEVER
self.run(is_timer=True)
if self._should_stop or self.interval <= 0:
return self.reactor.NEVER
return eventtime + self.interval
def run(self, repeat=True, is_timer=False):
if not is_timer:
self._should_stop = False
if self.delay is not None:
if self.delay > 0:
self.reactor.update_timer(self._timer,
self.reactor.monotonic()
+ self.delay)
else:
self.reactor.update_timer(self._timer, self.reactor.NOW)
return
swrap = self._template.create_status_wrapper()
context = { 'printer': swrap,
'is_timer': is_timer }
context.update(self._context)
result = self._template.render(context).strip()
if self.run_as_gcode and len(result) > 0:
self.gcode.run_script_from_command(result)
if not is_timer and self.interval > 0:
if repeat:
next_run = self.reactor.monotonic() + self.interval
else:
next_run = self.reactor.NEVER
self.reactor.update_timer(self._timer, next_run)
return
if self.trigger == "enter_pre":
if len(result) == 0:
return True
commands = [ c.strip() for c in result.split('\n') ]
return not "DGUS_ABORT_PAGE_SWITCH" in commands
def stop(self):
self._should_stop = True
self.reactor.update_timer(self._timer, self.reactor.NEVER)

View File

@ -0,0 +1,891 @@
# Support for DGUS T5UID1 touchscreens
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os, logging, struct, textwrap
import jinja2
import mcu
from . import var, page, routine, dgus_reloaded
from .. import gcode_macro, heaters
T5UID1_firmware_cfg = {
'dgus_reloaded': dgus_reloaded.configuration
}
DEFAULT_VOLUME = 75
DEFAULT_BRIGHTNESS = 100
T5UID1_CMD_WRITEVAR = 0x82
T5UID1_CMD_READVAR = 0x83
T5UID1_ADDR_VERSION = 0x0f
T5UID1_ADDR_BRIGHTNESS = 0x82
T5UID1_ADDR_PAGE = 0x84
T5UID1_ADDR_SOUND = 0xa0
T5UID1_ADDR_VOLUME = 0xa1
T5UID1_ADDR_CONTROL = 0xb0
TIMEOUT_SECS = 15
CMD_DELAY = 0.02
CONTROL_TYPES = {
'variable_data_input': 0x00,
'popup_window': 0x01,
'incremental_adjust': 0x02,
'slider_adjust': 0x03,
'rtc_settings': 0x04,
'return_key_code': 0x05,
'text_input': 0x06,
'firmware_settings': 0x07
}
def map_value_range(x, in_min, in_max, out_min, out_max):
return int(round((x - in_min)
* (out_max - out_min)
/ (in_max - in_min)
+ out_min))
def get_duration(seconds):
if type(seconds) is not int:
seconds = int(seconds)
if seconds < 0:
seconds = 0
minutes = seconds / 60
hours = minutes / 60
days = hours / 24
days %= 365
hours %= 24
minutes %= 60
seconds %= 60
result = str(seconds) + "s"
if minutes:
result = str(minutes) + "m " + result
if hours:
result = str(hours) + "h " + result
if days:
result = str(days) + "d " + result
return result
def bitwise_and(lhs, rhs):
return lhs & rhs
def bitwise_or(lhs, rhs):
return lhs | rhs
class T5UID1GCodeMacro:
def __init__(self, config):
self.printer = config.get_printer()
self.env = jinja2.Environment('{%', '%}', '{', '}',
trim_blocks=True,
lstrip_blocks=True,
extensions=['jinja2.ext.do'])
def load_template(self, config, option, default=None):
name = "%s:%s" % (config.get_name(), option)
if default is None:
script = config.get(option)
else:
script = config.get(option, default)
return gcode_macro.TemplateWrapper(self.printer, self.env, name, script)
class T5UID1:
def __init__(self, config):
self.printer = config.get_printer()
self.name = config.get_name()
self.reactor = self.printer.get_reactor()
self.gcode = self.printer.lookup_object('gcode')
self.configfile = self.printer.lookup_object('configfile')
self.stepper_enable = self.heaters = self.bed_mesh = None
self.toolhead = self.probe = self.pause_resume = None
self.extruders = {}
self.mcu = mcu.get_printer_mcu(self.printer,
config.get('t5uid1_mcu', 'mcu'))
self.oid = self.mcu.create_oid()
self._version = self.printer.get_start_args().get('software_version')
self._gcode_macro = T5UID1GCodeMacro(config)
firmware_cfg = config.getchoice('firmware', T5UID1_firmware_cfg)
self._firmware = config.get('firmware')
self._machine_name = config.get('machine_name', 'Generic 3D printer')
self._baud = config.getint('baud', 115200, minval=1200, maxval=921600)
self._update_interval = config.getint('update_interval', 2,
minval=1, maxval=10)
self._volume = config.getint('volume', DEFAULT_VOLUME,
minval=0, maxval=100)
self._brightness = config.getint('brightness', DEFAULT_BRIGHTNESS,
minval=0, maxval=100)
self._boot_sound = config.getint('boot_sound',
firmware_cfg['boot_sound'],
minval=-1, maxval=255)
self._notification_sound = config.getint('notification_sound',
firmware_cfg['notification_sound'], minval=-1, maxval=255)
self._z_min = config.getfloat('z_min', None)
self._z_max = config.getfloat('z_max', None)
self._last_cmd_time = 0
self._gui_version = 0
self._os_version = 0
self._current_page = ""
self._variable_data = {}
self._status_data = {}
self._vars = {}
self._pages = {}
self._routines = {}
self._is_printing = False
self._print_progress = 0
self._print_start_time = -1
self._print_pause_time = -1
self._print_end_time = -1
self._boot_page = self._timeout_page = self._shutdown_page = None
self._t5uid1_ping_cmd = self._t5uid1_write_cmd = None
self._is_connected = False
global_context = {
'get_variable': self.get_variable,
'set_variable': self.set_variable,
'enable_control': self.enable_control,
'disable_control': self.disable_control,
'start_routine': self.start_routine,
'stop_routine': self.stop_routine,
'set_message': self.set_message,
'bitwise_and': bitwise_and,
'bitwise_or': bitwise_or
}
context_input = dict(global_context)
context_input.update({
'page_name': self.page_name,
'switch_page': self.switch_page,
'play_sound': self.play_sound,
'set_volume': self.set_volume,
'set_brightness': self.set_brightness,
'limit_extrude': self.limit_extrude,
'heater_min_temp': self.heater_min_temp,
'heater_max_temp': self.heater_max_temp,
'heater_min_extrude_temp': self.heater_min_extrude_temp,
'is_busy': self.is_busy
})
context_output = dict(global_context)
context_output.update({
'all_steppers_enabled': self.all_steppers_enabled,
'heater_min_temp': self.heater_min_temp,
'heater_max_temp': self.heater_max_temp,
'probed_matrix': self.probed_matrix,
'pid_param': self.pid_param,
'get_duration': get_duration
})
context_routine = dict(global_context)
context_routine.update({
'page_name': self.page_name,
'switch_page': self.switch_page,
'play_sound': self.play_sound,
'set_volume': self.set_volume,
'set_brightness': self.set_brightness,
'abort_page_switch': self.abort_page_switch,
'full_update': self.full_update,
'is_busy': self.is_busy,
'check_paused': self.check_paused
})
self._status_data.update({
'controls': firmware_cfg['controls'],
'constants': firmware_cfg['constants']
})
self._load_config(config,
firmware_cfg['config_files'],
context_input,
context_output,
context_routine)
if self._boot_page is None:
raise self.printer.config_error("No boot page found")
if self._timeout_page is None:
self._timeout_page = self._boot_page
if self._shutdown_page is None:
self._shutdown_page = self._boot_page
self.mcu.register_config_callback(self._build_config)
self._update_timer = self.reactor.register_timer(self._send_update)
self._ping_timer = self.reactor.register_timer(self._do_ping)
self.gcode.register_command(
'DGUS_ABORT_PAGE_SWITCH', self.cmd_DGUS_ABORT_PAGE_SWITCH)
self.gcode.register_command(
'DGUS_PLAY_SOUND', self.cmd_DGUS_PLAY_SOUND)
self.gcode.register_command(
'DGUS_PRINT_START', self.cmd_DGUS_PRINT_START)
self.gcode.register_command(
'DGUS_PRINT_END', self.cmd_DGUS_PRINT_END)
self.gcode.register_command('M73', self.cmd_M73)
self.gcode.register_command('M117', self.cmd_M117)
self.gcode.register_command('M300', self.cmd_M300)
self.printer.register_event_handler("klippy:ready",
self._handle_ready)
self.printer.register_event_handler("klippy:shutdown",
self._handle_shutdown)
self.printer.register_event_handler("klippy:disconnect",
self._handle_disconnect)
def _load_config(self, config, fnames, ctx_in, ctx_out, ctx_routine):
if type(fnames) is not list:
fnames = [fnames]
v_list = config.get_prefix_sections('t5uid1_var ')
v_main_names = { c.get_name(): 1 for c in v_list }
p_list = config.get_prefix_sections('t5uid1_page ')
p_main_names = { c.get_name(): 1 for c in p_list }
r_list = config.get_prefix_sections('t5uid1_routine ')
r_main_names = { c.get_name(): 1 for c in r_list }
for fname in fnames:
filepath = os.path.join(os.path.dirname(__file__),
self._firmware,
fname)
try:
dconfig = self.configfile.read_config(filepath)
except Exception:
raise self.printer.config_error("Cannot load config '%s'"
% (filepath,))
v_list += [c for c in dconfig.get_prefix_sections('t5uid1_var ')
if c.get_name() not in v_main_names]
p_list += [c for c in dconfig.get_prefix_sections('t5uid1_page ')
if c.get_name() not in p_main_names]
r_list += [c for c in dconfig.get_prefix_sections('t5uid1_routine ')
if c.get_name() not in r_main_names]
for c in v_list:
v = var.T5UID1_Var(self._gcode_macro,
ctx_in,
ctx_out,
c)
if v.name in self._vars:
raise self.printer.config_error("t5uid1_var '%s' already"
" exists" % (v.name,))
self._vars[v.name] = v
for c in p_list:
p = page.T5UID1_Page(self._vars.keys(), c)
if p.name in self._pages:
raise self.printer.config_error("t5uid1_page '%s' already"
" exists" % (p.name,))
self._pages[p.name] = p
if p.is_boot:
if self._boot_page is None:
self._boot_page = p.name
else:
raise self.printer.config_error("Multiple boot pages"
" found")
if p.is_timeout:
if self._timeout_page is None:
self._timeout_page = p.name
else:
raise self.printer.config_error("Multiple timeout pages"
" found")
if p.is_shutdown:
if self._shutdown_page is None:
self._shutdown_page = p.name
else:
raise self.printer.config_error("Multiple shutdown pages"
" found")
for c in r_list:
r = routine.T5UID1_Routine(self._gcode_macro,
ctx_routine,
self._pages.keys(),
c)
if r.name in self._routines:
raise self.printer.config_error("t5uid1_routine '%s' already"
" exists" % (r.name,))
self._routines[r.name] = r
def _build_config(self):
timeout_command, timeout_data = self.switch_page(self._timeout_page,
send=False)
timeout_data = "".join(["%02x" % (x,) for x in timeout_data])
self.mcu.add_config_cmd(
"config_t5uid1 oid=%d baud=%d timeout=%d"
" timeout_command=%d timeout_data=%s"
% (self.oid, self._baud, TIMEOUT_SECS,
timeout_command, timeout_data))
curtime = self.reactor.monotonic()
self._last_cmd_time = self.mcu.estimated_print_time(curtime)
cmd_queue = self.mcu.alloc_command_queue()
self._t5uid1_ping_cmd = self.mcu.lookup_command(
"t5uid1_ping oid=%c", cq=cmd_queue)
self._t5uid1_write_cmd = self.mcu.lookup_command(
"t5uid1_write oid=%c command=%c data=%*s", cq=cmd_queue)
self.mcu.register_response(self._handle_t5uid1_received,
"t5uid1_received")
def _handle_ready(self):
has_bltouch = False
try:
self.printer.lookup_object('bltouch')
has_bltouch = True
except Exception:
pass
self._status_data.update({
'limits': self.limits(),
'has_bltouch': has_bltouch
})
self._is_connected = True
self.reactor.register_timer(self._on_ready, self.reactor.NOW)
def _on_ready(self, eventtime):
if not self._is_connected:
return self.reactor.NEVER
self._last_cmd_time = self.mcu.estimated_print_time(eventtime)
self.t5uid1_command_read(T5UID1_ADDR_VERSION, 1)
self.set_brightness(self._brightness)
self.switch_page(self._boot_page)
if self._boot_sound >= 0:
self.play_sound(self._boot_sound, volume=self._volume)
else:
self.set_volume(self._volume)
return self.reactor.NEVER
def _handle_shutdown(self):
msg = getattr(self.mcu, "_shutdown_msg", "").strip()
parts = textwrap.wrap(msg, 32)
while len(parts) < 4:
parts.append("")
self.set_variable("line1", parts[0].strip())
self.set_variable("line2", parts[1].strip())
self.set_variable("line3", parts[2].strip())
self.set_variable("line4", parts[3].strip())
self.switch_page(self._shutdown_page)
if self._notification_sound >= 0:
self.play_sound(self._notification_sound)
def _handle_disconnect(self):
self._is_connected = False
self._current_page = ""
self.reactor.update_timer(self._update_timer, self.reactor.NEVER)
self.reactor.update_timer(self._ping_timer, self.reactor.NEVER)
def _handle_t5uid1_received(self, params):
if not self._is_connected:
return
logging.debug("t5uid1_received %s", params)
if params['command'] != T5UID1_CMD_READVAR:
return
data = bytearray(params['data'])
if len(data) < 3:
logging.warning("Received invalid T5UID1 message")
return
address = struct.unpack(">H", data[:2])[0]
data_len = data[2] << 1
if len(data) < data_len + 3:
logging.warning("Received invalid T5UID1 message")
return
data = data[3:data_len + 3]
self.reactor.register_async_callback(
(lambda e, s=self, a=address, d=data: s.handle_received(a, d)))
def handle_received(self, address, data):
if not self._is_connected:
return
if address == T5UID1_ADDR_VERSION and len(data) == 2:
self._gui_version = data[0]
self._os_version = data[1]
return
handled = False
for name in self._vars:
if (self._vars[name].address != address
or self._vars[name].type != "input"):
continue
handled = True
try:
self._vars[name].data_received(data)
except ValueError as e:
logging.exception("Exception in '%s' receive handler: %s"
% (name, str(e)))
except Exception as e:
logging.exception("Unhandled exception in '%s' receive"
" handler: %s" % (name, str(e)))
if not handled:
logging.info("Received unhandled T5UID1 message for address %s"
% (hex(address),))
def send_var(self, name):
if name not in self._vars:
raise Exception("T5UID1_Var '%s' not found" % (name,))
return self.t5uid1_command_write(self._vars[name].address,
self._vars[name].prepare_data())
def page_name(self, page_id):
if type(page_id) is not int:
page_id = int(page_id)
for name in self._pages:
if self._pages[name].id == page_id:
return name
raise Exception("T5UID1_Page %d not found" % (page_id,))
def send_page_vars(self, page=None, complete=False):
if page is None:
page = self._current_page
if page not in self._pages:
raise Exception("T5UID1_Page '%s' not found" % (page,))
if complete:
for var_name in self._pages[page].var:
self.send_var(var_name)
for var_name in self._pages[page].var_auto:
self.send_var(var_name)
def full_update(self):
self.send_page_vars(complete=True)
self.reactor.update_timer(self._update_timer,
self.reactor.monotonic()
+ self._update_interval)
def start_routine(self, routine):
if routine not in self._routines:
raise Exception("T5UID1_Routine '%s' not found" % (routine,))
if self._routines[routine].trigger != "manual":
raise Exception("T5UID1_Routine '%s' cannot be started manually"
% (routine,))
self._routines[routine].run()
def stop_routine(self, routine):
if routine not in self._routines:
raise Exception("T5UID1_Routine '%s' not found" % (routine,))
self._routines[routine].stop()
def _start_page_routines(self, page, trigger):
if page not in self._pages:
raise Exception("T5UID1_Page '%s' not found" % (page,))
results = []
for routine in self._routines:
if (self._routines[routine].page != page
or self._routines[routine].trigger != trigger):
continue
result = self._routines[routine].run()
if result is not None:
results.append(result)
return all(results)
def _stop_page_routines(self, page):
if page not in self._pages:
raise Exception("T5UID1_Page '%s' not found" % (page,))
for routine in self._routines:
if self._routines[routine].page != page:
continue
self._routines[routine].stop()
class sentinel: pass
def get_variable(self, name, default=sentinel):
if name not in self._variable_data:
if default is not self.sentinel:
return default
raise Exception("Variable '%s' not found" % (name,))
return self._variable_data[name]
def set_variable(self, name, value):
self._variable_data[name] = value
def check_paused(self):
if not self._is_printing:
return
if self.pause_resume is None:
try:
self.pause_resume = self.printer.lookup_object('pause_resume')
except Exception:
return
curtime = self.reactor.monotonic()
if self._print_pause_time < 0 and self.pause_resume.is_paused:
self._print_pause_time = curtime
elif self._print_pause_time >= 0 and not self.pause_resume.is_paused:
pause_duration = curtime - self._print_pause_time
if pause_duration > 0:
self._print_start_time += pause_duration
self._print_pause_time = -1
def get_status(self, eventtime):
pages = { p: self._pages[p].id for p in self._pages }
res = dict(self._status_data)
if not self._is_printing:
print_duration = self._print_end_time - self._print_start_time
elif self._print_pause_time >= 0:
print_duration = self._print_pause_time - self._print_start_time
else:
print_duration = eventtime - self._print_start_time
res.update({
'version': self._version,
'machine_name': self._machine_name,
'gui_version': self._gui_version,
'os_version': self._os_version,
'notification_sound': self._notification_sound,
'page': self._current_page,
'volume': self._volume,
'brightness': self._brightness,
'pages': pages,
'control_types': CONTROL_TYPES,
'is_printing': self._is_printing,
'print_progress': self._print_progress,
'print_duration': max(0, print_duration)
})
return res
def _send_update(self, eventtime):
if not self._is_connected or not self._current_page:
return self.reactor.NEVER
try:
self.send_page_vars(self._current_page, complete=False)
except Exception as e:
logging.exception("Unhandled exception in update timer: %s"
% (name, str(e)))
return eventtime + self._update_interval
def _do_ping(self, eventtime):
if not self._is_connected or self._t5uid1_ping_cmd is None:
return self.reactor.NEVER
print_time = self.mcu.estimated_print_time(eventtime)
print_time = max(self._last_cmd_time + CMD_DELAY, print_time)
clock = self.mcu.print_time_to_clock(print_time)
self._t5uid1_ping_cmd.send([self.oid], minclock=clock)
self._last_cmd_time = print_time
return eventtime + TIMEOUT_SECS - 2
def _t5uid1_write(self, command, data, schedule_ping=True):
if not self._is_connected or self._t5uid1_write_cmd is None:
return
curtime = self.reactor.monotonic()
print_time = self.mcu.estimated_print_time(curtime)
print_time = max(self._last_cmd_time + CMD_DELAY, print_time)
clock = self.mcu.print_time_to_clock(print_time)
self._t5uid1_write_cmd.send([self.oid, command, list(data)],
minclock=clock)
self._last_cmd_time = print_time
if schedule_ping:
self.reactor.update_timer(self._ping_timer,
curtime + TIMEOUT_SECS - 2)
def t5uid1_command_write(self, address, data, send=True):
if address < 0 or address > 0xffff:
raise ValueError("invalid address")
if type(data) is not bytearray:
raise ValueError("invalid data")
if len(data) < 1 or len(data) > 64 or len(data) % 2 != 0:
raise ValueError("invalid data length")
command = T5UID1_CMD_WRITEVAR
command_data = bytearray([ (address >> 8), (address & 0xff) ])
command_data.extend(data)
if not send:
return (command, command_data)
self._t5uid1_write(command, command_data)
def t5uid1_command_read(self, address, wlen, send=True):
if address < 0 or address > 0xffff:
raise ValueError("invalid address")
if wlen < 1 or wlen > 0x7d:
raise ValueError("invalid wlen")
command = T5UID1_CMD_READVAR
command_data = bytearray([ (address >> 8), (address & 0xff), wlen ])
if not send:
return (command, command_data)
self._t5uid1_write(command, command_data)
def switch_page(self, name, send=True):
if name not in self._pages:
raise ValueError("invalid page")
if not send:
return self.t5uid1_command_write(T5UID1_ADDR_PAGE,
bytearray([
0x5a, 0x01,
0x00, self._pages[name].id
]),
send)
if name == self._current_page:
return
if not self._start_page_routines(name, "enter_pre"):
return
self.send_page_vars(name, complete=True)
self.t5uid1_command_write(T5UID1_ADDR_PAGE,
bytearray([
0x5a, 0x01,
0x00, self._pages[name].id
]),
send)
if self._current_page:
self._stop_page_routines(self._current_page)
self._start_page_routines(self._current_page, "leave")
self._current_page = name
self._start_page_routines(name, "enter")
self.reactor.update_timer(self._update_timer,
self.reactor.monotonic()
+ self._update_interval)
def abort_page_switch(self):
return "DGUS_ABORT_PAGE_SWITCH"
def play_sound(self, start, slen=1, volume=-1, send=True):
if start < 0 or start > 255:
raise ValueError("invalid start")
if slen < 1 or slen > 255:
raise ValueError("invalid slen")
if volume > 100:
raise ValueError("invalid volume")
if volume < 0:
volume = self._volume
val = map_value_range(volume, 0, 100, 0, 255)
return self.t5uid1_command_write(T5UID1_ADDR_SOUND,
bytearray([start, slen, val, 0]),
send)
def enable_control(self, page, ctype, control, send=True):
if page < 0 or page > 255:
raise ValueError("invalid page")
if ctype < 0 or ctype > 255:
raise ValueError("invalid ctype")
if control < 0 or control > 255:
raise ValueError("invalid control")
return self.t5uid1_command_write(T5UID1_ADDR_CONTROL,
bytearray([
0x5a, 0xa5, 0, page,
control, ctype, 0, 0x01
]),
send)
def disable_control(self, page, ctype, control, send=True):
if page < 0 or page > 255:
raise ValueError("invalid page")
if ctype < 0 or ctype > 255:
raise ValueError("invalid ctype")
if control < 0 or control > 255:
raise ValueError("invalid control")
return self.t5uid1_command_write(T5UID1_ADDR_CONTROL,
bytearray([
0x5a, 0xa5, 0, page,
control, ctype, 0, 0
]),
send)
def set_brightness(self, brightness, send=True):
if brightness < 0 or brightness > 100:
raise ValueError("invalid brightness")
val = map_value_range(brightness, 0, 100, 5, 100)
result = self.t5uid1_command_write(T5UID1_ADDR_BRIGHTNESS,
bytearray([val, val]),
send)
if not send:
return result
if self._brightness != brightness:
self._brightness = brightness
self.configfile.set(self.name, 'brightness', brightness)
def set_volume(self, volume, send=True):
if volume < 0 or volume > 100:
raise ValueError("invalid volume")
val = map_value_range(volume, 0, 100, 0, 255)
result = self.t5uid1_command_write(T5UID1_ADDR_VOLUME,
bytearray([val, 0]),
send)
if not send:
return result
if self._volume != volume:
self._volume = volume
self.configfile.set(self.name, 'volume', volume)
def all_steppers_enabled(self):
if self.stepper_enable is None:
self.stepper_enable = self.printer.lookup_object('stepper_enable')
res = True
for name in ['stepper_x', 'stepper_y', 'stepper_z']:
res &= self.stepper_enable.lookup_enable(name).is_motor_enabled()
return res
def heater_min_temp(self, heater):
if self.heaters is None:
self.heaters = self.printer.lookup_object('heaters')
try:
return self.heaters.lookup_heater(heater).min_temp
except Exception:
return 0
def heater_max_temp(self, heater, margin=0):
if self.heaters is None:
self.heaters = self.printer.lookup_object('heaters')
try:
return max(0, self.heaters.lookup_heater(heater).max_temp - margin)
except Exception:
return 0
def heater_min_extrude_temp(self, heater):
if self.heaters is None:
self.heaters = self.printer.lookup_object('heaters')
return self.heaters.lookup_heater(heater).min_extrude_temp
def probed_matrix(self):
if self.bed_mesh is None:
self.bed_mesh = self.printer.lookup_object('bed_mesh')
count = len(self.bed_mesh.bmc.probe_helper.results)
points_map = [ 0, 1, 2, 3, 4,
9, 8, 7, 6, 5,
10, 11, 12, 13, 14,
19, 18, 17, 16, 15,
20, 21, 22, 23, 24]
res = 0
for i in range(25):
if count > points_map[i]:
if i < 16:
res |= 1 << (i + 16)
else:
res |= 1 << (i - 16)
return res
def pid_param(self, heater, param):
if param not in ['p', 'i', 'd']:
raise ValueError("Invalid param")
if self.heaters is None:
self.heaters = self.printer.lookup_object('heaters')
try:
return getattr(self.heaters.lookup_heater(heater).control,
'K' + param) * heaters.PID_PARAM_BASE
except Exception:
return 0
def limit_extrude(self, extruder, val):
try:
if extruder in self.extruders:
res = self.extruders[extruder].max_e_dist
else:
e = self.printer.lookup_object(extruder)
res = e.max_e_dist
self.extruders[extruder] = e
return min(res, val)
except Exception:
return 0
def limits(self):
if self.toolhead is None:
self.toolhead = self.printer.lookup_object('toolhead')
kin = self.toolhead.get_kinematics()
x_min, x_max = kin.rails[0].get_range()
y_min, y_max = kin.rails[1].get_range()
z_min, z_max = kin.rails[2].get_range()
if (self._z_min is not None
and self._z_min > z_min
and self._z_min < z_max):
z_min = self._z_min
if (self._z_max is not None
and self._z_max < z_max
and self._z_max > z_min):
z_max = self._z_max
return {
'x_min': x_min,
'x_max': x_max,
'y_min': y_min,
'y_max': y_max,
'z_min': z_min,
'z_max': z_max
}
def set_message(self, message):
self.set_variable('message', message)
if 'message' in self._vars:
self.send_var('message')
if len(message) > 0 and 'message_timeout' in self._routines:
self.start_routine('message_timeout')
def is_busy(self):
if self.toolhead is None:
self.toolhead = self.printer.lookup_object('toolhead')
if self.probe is None:
self.probe = self.printer.lookup_object('probe')
eventtime = self.reactor.monotonic()
print_time, est_print_time, lookahead_empty = self.toolhead.check_busy(
eventtime)
idle_time = est_print_time - print_time
return (not lookahead_empty
or idle_time < 1.0
or self.gcode.get_mutex().test()
or self.probe.multi_probe_pending)
def cmd_DGUS_ABORT_PAGE_SWITCH(self, gcmd):
pass
def cmd_DGUS_PLAY_SOUND(self, gcmd):
if self._notification_sound >= 0:
start = gcmd.get_int('START', self._notification_sound,
minval=0, maxval=255)
else:
start = gcmd.get_int('START', minval=0, maxval=255)
slen = gcmd.get_int('LEN', 1, minval=0, maxval=255)
volume = gcmd.get_int('VOLUME', -1, minval=0, maxval=100)
try:
self.play_sound(start, slen, volume)
except Exception as e:
raise gcmd.error(str(e))
gcmd.respond_info("Playing sound %d (len=%d, volume=%d)"
% (start, slen, volume))
def cmd_DGUS_PRINT_START(self, gcmd):
self._print_progress = 0
self._print_start_time = self.reactor.monotonic()
self._print_pause_time = -1
self._print_end_time = -1
self._is_printing = True
self.check_paused()
if 'print_start' in self._routines:
self.start_routine('print_start')
def cmd_DGUS_PRINT_END(self, gcmd):
if not self._is_printing:
return
self._print_progress = 100
curtime = self.reactor.monotonic()
if self._print_pause_time >= 0:
pause_duration = curtime - self._print_pause_time
if pause_duration > 0:
self._print_start_time += pause_duration
self._print_pause_time = -1
self._print_end_time = curtime
self._is_printing = False
if 'print_end' in self._routines:
self.start_routine('print_end')
def cmd_M73(self, gcmd):
progress = gcmd.get_int('P', 0)
self._print_progress = min(100, max(0, progress))
def cmd_M117(self, gcmd):
msg = gcmd.get_commandline()
umsg = msg.upper()
if not umsg.startswith('M117'):
start = umsg.find('M117')
end = msg.rfind('*')
msg = msg[start:end]
if len(msg) > 5:
self.set_message(msg[5:])
else:
self.set_message("")
def cmd_M300(self, gcmd):
if self._notification_sound >= 0:
start = gcmd.get_int('S', self._notification_sound)
else:
start = gcmd.get_int('S', minval=0, maxval=255)
slen = gcmd.get_int('P', 1, minval=1, maxval=255)
volume = gcmd.get_int('V', -1, minval=0, maxval=100)
if start < 0 or start > 255:
start = self._notification_sound
try:
self.play_sound(start, slen, volume)
except Exception as e:
raise gcmd.error(str(e))
def load_config(config):
return T5UID1(config)

154
klippy/extras/t5uid1/var.py Normal file
View File

@ -0,0 +1,154 @@
# Variable class
#
# Copyright (C) 2020 Desuuuu <contact@desuuuu.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import struct
TYPES_LEN = {
'int16': 2,
'uint16': 2,
'int32': 4,
'uint32': 4,
'float': 4
}
TYPES_FMT = {
'int16': '>h',
'uint16': '>H',
'int32': '>i',
'uint32': '>I',
'float': '>f'
}
class T5UID1_Var:
def __init__(self, gcode_macro, input_context, output_context, config):
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
name_parts = config.get_name().split()
if len(name_parts) != 2:
raise config.error("Section name '%s' is not valid"
% (config.get_name(),))
self.name = name_parts[1]
self.type = config.get('type')
if self.type not in ["input", "output"]:
raise config.error("Invalid type '%s' in section '%s'"
% (self.type, config.get_name()))
address = config.get('address')
try:
self.address = int(address, 16)
if self.address < 0x1000 or self.address > 0xffff:
raise
except:
raise config.error("Invalid address '%s' in section '%s'"
% (address, config.get_name()))
data_types = TYPES_LEN.keys()
data_types.append('str')
if self.type == "input":
data_types.append('none')
self.data_type = config.get('data_type')
if (self.data_type.startswith('array[')
and self.data_type.endswith(']')
and self.type == "output"
and self.data_type != "str"):
self.data_type = self.data_type[6:-1]
self.array_len = config.getint('array_len', minval=2, maxval=32)
else:
self.array_len = 1
if self.data_type not in data_types:
raise config.error("Invalid data_type '%s' in section '%s'"
% (self.data_type, config.get_name()))
if self.data_type == "none":
self.data_len = 0
elif self.data_type == "str":
self.data_len = config.getint('data_len', minval=1, maxval=32)
else:
self.data_len = TYPES_LEN[self.data_type]
if self.type == "input":
self._template = gcode_macro.load_template(config, 'script')
self._context = input_context
self.run_as_gcode = config.getboolean('run_as_gcode', False)
elif self.type == "output":
self._template = gcode_macro.load_template(config, 'script')
self._context = output_context
self.run_as_gcode = False
def data_received(self, data):
if self.type != "input":
raise Exception("not an input")
swrap = self._template.create_status_wrapper()
context = { 'printer': swrap }
context.update(self._context)
if self.data_type != "none" and self.data_len != 0:
received_len = len(data)
if self.data_type == "str":
buf = bytearray()
for i in range(received_len):
if i >= self.data_len:
break
if data[i] == 0x00:
break
if (i + 1 < received_len
and data[i] == 0xff and data[i + 1] == 0xff):
break
buf.append(data[i])
data = str(buf.decode('ascii'))
elif received_len != self.data_len:
raise ValueError("Expected %d bytes, got %d"
% (self.data_len, received_len))
else:
data = struct.unpack(TYPES_FMT[self.data_type], data)[0]
context.update({ 'data': data })
result = self._template.render(context).strip()
if self.run_as_gcode and len(result) > 0:
self.gcode.run_script_from_command(result)
def prepare_data(self):
if self.type != "output":
raise Exception("not an output")
swrap = self._template.create_status_wrapper()
context = { 'printer': swrap }
context.update(self._context)
result = self._template.render(context)
if self.data_type == "str":
result = bytearray(result.replace('\n', ''))
target_len = self.data_len
if target_len % 2 != 0:
target_len += 1
extra_bytes = len(result) - target_len
if extra_bytes > 0:
result = result[:target_len]
elif extra_bytes < 0:
result.extend([0] * abs(extra_bytes))
else:
parts = result.strip().split(',')
result = bytearray()
count = 0
for part in parts:
part = part.strip()
if len(part) == 0:
continue
if self.data_type == "float":
part = float(part)
else:
part = int(part)
result.extend(struct.pack(TYPES_FMT[self.data_type], part))
count += 1
if count != self.array_len:
raise ValueError("Expected %d values, got %d"
% (self.array_len, count))
return result

View File

@ -102,6 +102,34 @@ config AVR_STACK_SIZE
config AVR_WATCHDOG config AVR_WATCHDOG
bool bool
default y default y
config T5UID1_SERIAL
depends on MACH_atmega2560
bool "Enable DGUS T5UID1 screen"
default n
choice
depends on T5UID1_SERIAL
prompt "Screen Serial Port" if LOW_LEVEL_OPTIONS
default T5UID1_SERIAL_UART2
help
Select the serial device to use for the touchscreen.
config T5UID1_SERIAL_UART0
bool "UART0"
config T5UID1_SERIAL_UART1
bool "UART1"
config T5UID1_SERIAL_UART2
bool "UART2"
config T5UID1_SERIAL_UART3
bool "UART3"
endchoice
config T5UID1_SERIAL_PORT
depends on T5UID1_SERIAL
int
default 3 if T5UID1_SERIAL_UART3
default 1 if T5UID1_SERIAL_UART1
default 0 if T5UID1_SERIAL_UART0
default 2
config USBSERIAL config USBSERIAL
bool "Use USB for communication (instead of serial)" bool "Use USB for communication (instead of serial)"
depends on MACH_at90usb1286 || MACH_at90usb646 || MACH_atmega32u4 depends on MACH_at90usb1286 || MACH_at90usb646 || MACH_atmega32u4

View File

@ -17,6 +17,8 @@ src-$(CONFIG_HAVE_GPIO_HARD_PWM) += avr/hard_pwm.c
src-$(CONFIG_AVR_WATCHDOG) += avr/watchdog.c src-$(CONFIG_AVR_WATCHDOG) += avr/watchdog.c
src-$(CONFIG_USBSERIAL) += avr/usbserial.c generic/usb_cdc.c src-$(CONFIG_USBSERIAL) += avr/usbserial.c generic/usb_cdc.c
src-$(CONFIG_SERIAL) += avr/serial.c generic/serial_irq.c src-$(CONFIG_SERIAL) += avr/serial.c generic/serial_irq.c
src-$(CONFIG_T5UID1_SERIAL) += avr/serial_t5uid1.c
src-$(CONFIG_T5UID1_SERIAL) += generic/t5uid1_irq.c
# Suppress broken "misspelled signal handler" warnings on gcc 4.8.1 # Suppress broken "misspelled signal handler" warnings on gcc 4.8.1
CFLAGS_klipper.elf := $(CFLAGS_klipper.elf) $(if $(filter 4.8.1, $(shell $(CC) -dumpversion)), -w) CFLAGS_klipper.elf := $(CFLAGS_klipper.elf) $(if $(filter 4.8.1, $(shell $(CC) -dumpversion)), -w)

93
src/avr/serial_t5uid1.c Normal file
View File

@ -0,0 +1,93 @@
// AVR serial port code.
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <avr/interrupt.h> // USART_RX_vect
#include "autoconf.h" // CONFIG_T5UID1_SERIAL_PORT
#include "board/t5uid1_irq.h" // t5uid1_rx_byte
#include "command.h" // DECL_CONSTANT_STR
#include "sched.h" // DECL_INIT
#if CONFIG_T5UID1_SERIAL_PORT == CONFIG_SERIAL_PORT
#error "The serial port selected for the T5UID1 screen is already used"
#endif
// Reserve serial pins
#if CONFIG_T5UID1_SERIAL_PORT == 0
#if CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "PE0,PE1");
#else
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "PD0,PD1");
#endif
#elif CONFIG_T5UID1_SERIAL_PORT == 1
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "PD2,PD3");
#elif CONFIG_T5UID1_SERIAL_PORT == 2
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "PH0,PH1");
#else
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "PJ0,PJ1");
#endif
// Helper macros for defining serial port aliases
#define AVR_SERIAL_REG1(prefix, id, suffix) prefix ## id ## suffix
#define AVR_SERIAL_REG(prefix, id, suffix) AVR_SERIAL_REG1(prefix, id, suffix)
// Serial port register aliases
#define UCSRxA AVR_SERIAL_REG(UCSR, CONFIG_T5UID1_SERIAL_PORT, A)
#define UCSRxB AVR_SERIAL_REG(UCSR, CONFIG_T5UID1_SERIAL_PORT, B)
#define UCSRxC AVR_SERIAL_REG(UCSR, CONFIG_T5UID1_SERIAL_PORT, C)
#define UBRRx AVR_SERIAL_REG(UBRR, CONFIG_T5UID1_SERIAL_PORT,)
#define UDRx AVR_SERIAL_REG(UDR, CONFIG_T5UID1_SERIAL_PORT,)
#define UCSZx1 AVR_SERIAL_REG(UCSZ, CONFIG_T5UID1_SERIAL_PORT, 1)
#define UCSZx0 AVR_SERIAL_REG(UCSZ, CONFIG_T5UID1_SERIAL_PORT, 0)
#define U2Xx AVR_SERIAL_REG(U2X, CONFIG_T5UID1_SERIAL_PORT,)
#define RXENx AVR_SERIAL_REG(RXEN, CONFIG_T5UID1_SERIAL_PORT,)
#define TXENx AVR_SERIAL_REG(TXEN, CONFIG_T5UID1_SERIAL_PORT,)
#define RXCIEx AVR_SERIAL_REG(RXCIE, CONFIG_T5UID1_SERIAL_PORT,)
#define UDRIEx AVR_SERIAL_REG(UDRIE, CONFIG_T5UID1_SERIAL_PORT,)
#if defined(USART_RX_vect)
// The atmega168 / atmega328 doesn't have an ID in the irq names
#define USARTx_RX_vect USART_RX_vect
#define USARTx_UDRE_vect USART_UDRE_vect
#else
#define USARTx_RX_vect \
AVR_SERIAL_REG(USART, CONFIG_T5UID1_SERIAL_PORT, _RX_vect)
#define USARTx_UDRE_vect \
AVR_SERIAL_REG(USART, CONFIG_T5UID1_SERIAL_PORT, _UDRE_vect)
#endif
void
t5uid1_init(uint32_t baud)
{
UCSRxA = CONFIG_SERIAL_BAUD_U2X ? (1<<U2Xx) : 0;
uint32_t cm = CONFIG_SERIAL_BAUD_U2X ? 8 : 16;
UBRRx = DIV_ROUND_CLOSEST(CONFIG_CLOCK_FREQ, cm * baud) - 1UL;
UCSRxC = (1<<UCSZx1) | (1<<UCSZx0);
UCSRxB = (1<<RXENx) | (1<<TXENx) | (1<<RXCIEx) | (1<<UDRIEx);
}
// Rx interrupt - data available to be read.
ISR(USARTx_RX_vect)
{
t5uid1_rx_byte(UDRx);
}
// Tx interrupt - data can be written to serial.
ISR(USARTx_UDRE_vect)
{
uint8_t data;
int ret = t5uid1_get_tx_byte(&data);
if (ret)
UCSRxB &= ~(1<<UDRIEx);
else
UDRx = data;
}
// Enable tx interrupts
void
t5uid1_enable_tx_irq(void)
{
UCSRxB |= 1<<UDRIEx;
}

245
src/generic/t5uid1_irq.c Normal file
View File

@ -0,0 +1,245 @@
// Generic interrupt based serial uart helper code
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memmove
#include "basecmd.h" // oid_alloc
#include "board/io.h" // readb
#include "board/irq.h" // irq_save
#include "board/misc.h" // timer_read_time
#include "command.h" // DECL_CONSTANT
#include "sched.h" // sched_wake_task
#include "t5uid1_irq.h" // t5uid1_enable_tx_irq
#define T5UID1_HEADER1 0x5A
#define T5UID1_HEADER2 0xA5
#define T5UID1_HEADER_LEN 3
#define RX_BUFFER_SIZE 192
#define TIMER_MS 500
struct t5uid1 {
struct timer timer;
uint32_t baud;
uint16_t ticks;
uint16_t timeout;
uint8_t timeout_command;
uint8_t timeout_data_len;
uint8_t timeout_data[];
};
static uint8_t receive_buf[RX_BUFFER_SIZE], receive_pos;
static uint8_t transmit_buf[96], transmit_pos, transmit_max;
static struct task_wake t5uid1_wake;
void
t5uid1_send_command(uint_fast8_t command, uint8_t *data, uint_fast8_t data_len)
{
if (data_len < 1)
return;
// Verify space for message
uint_fast8_t tpos = readb(&transmit_pos), tmax = readb(&transmit_max);
if (tpos >= tmax) {
tpos = tmax = 0;
writeb(&transmit_max, 0);
writeb(&transmit_pos, 0);
}
uint_fast8_t msglen = T5UID1_HEADER_LEN + 1 + data_len;
if (tmax + msglen > sizeof(transmit_buf)) {
if (tmax + msglen - tpos > sizeof(transmit_buf))
// Not enough space for message
return;
// Disable TX irq and move buffer
writeb(&transmit_max, 0);
tpos = readb(&transmit_pos);
tmax -= tpos;
memmove(&transmit_buf[0], &transmit_buf[tpos], tmax);
writeb(&transmit_pos, 0);
writeb(&transmit_max, tmax);
t5uid1_enable_tx_irq();
}
// Generate message
transmit_buf[tmax] = T5UID1_HEADER1;
transmit_buf[tmax + 1] = T5UID1_HEADER2;
transmit_buf[tmax + 2] = data_len + 1;
transmit_buf[tmax + 3] = command;
memcpy(&transmit_buf[tmax + 4], data, data_len);
// Start message transmit
writeb(&transmit_max, tmax + msglen);
t5uid1_enable_tx_irq();
}
static uint_fast8_t
t5uid1_timeout_event(struct timer *timer)
{
struct t5uid1 *t = container_of(timer, struct t5uid1, timer);
if (++t->ticks >= t->timeout) {
if (t->timeout) {
t5uid1_send_command(t->timeout_command,
t->timeout_data, t->timeout_data_len);
}
t->ticks = UINT16_MAX;
return SF_DONE;
}
t->timer.waketime += timer_from_us(TIMER_MS * 1000);
return SF_RESCHEDULE;
}
void
reset_timer(struct t5uid1 *t)
{
if (!t->timeout) {
sched_del_timer(&t->timer);
} else if (t->ticks == UINT16_MAX) {
sched_del_timer(&t->timer);
uint32_t now = timer_read_time();
t->timer.waketime = now + timer_from_us(TIMER_MS * 1000);
t->timer.func = t5uid1_timeout_event;
if (!timer_is_before(t->timer.waketime, now)) {
t->ticks = 0;
sched_add_timer(&t->timer);
}
} else {
t->ticks = 0;
}
}
void
command_config_t5uid1(uint32_t *args)
{
uint8_t timeout_data_len = args[4];
struct t5uid1 *t = oid_alloc(
args[0], command_config_t5uid1, sizeof(*t) + timeout_data_len);
t->baud = args[1];
t->ticks = UINT16_MAX;
t->timeout = args[2] * (1000 / TIMER_MS);
t->timeout_command = args[3];
t->timeout_data_len = timeout_data_len;
uint8_t *timeout_data = (void*)(size_t)args[5];
memcpy(t->timeout_data, timeout_data, timeout_data_len);
t5uid1_init(t->baud);
reset_timer(t);
}
DECL_COMMAND(command_config_t5uid1,
"config_t5uid1 oid=%c baud=%u timeout=%hu"
" timeout_command=%c timeout_data=%*s");
void
command_t5uid1_ping(uint32_t *args)
{
struct t5uid1 *t = oid_lookup(args[0], command_config_t5uid1);
reset_timer(t);
}
DECL_COMMAND_FLAGS(command_t5uid1_ping, HF_IN_SHUTDOWN,
"t5uid1_ping oid=%c");
void
command_t5uid1_write(uint32_t *args)
{
struct t5uid1 *t = oid_lookup(args[0], command_config_t5uid1);
uint_fast8_t command = args[1];
uint_fast8_t data_len = args[2];
uint8_t *data = (void*)(size_t)args[3];
t5uid1_send_command(command, data, data_len);
reset_timer(t);
}
DECL_COMMAND_FLAGS(command_t5uid1_write, HF_IN_SHUTDOWN,
"t5uid1_write oid=%c command=%c data=%*s");
// Rx interrupt - store read data
void
t5uid1_rx_byte(uint_fast8_t data)
{
if (receive_pos > T5UID1_HEADER_LEN)
sched_wake_task(&t5uid1_wake);
if (receive_pos >= sizeof(receive_buf))
// Serial overflow - ignore it
return;
receive_buf[receive_pos++] = data;
}
// Tx interrupt - get next byte to transmit
int
t5uid1_get_tx_byte(uint8_t *pdata)
{
if (transmit_pos >= transmit_max)
return -1;
*pdata = transmit_buf[transmit_pos++];
return 0;
}
// Remove from the receive buffer the given number of bytes
static void
t5uid1_pop_input(uint_fast8_t len)
{
uint_fast8_t copied = 0;
for (;;) {
uint_fast8_t rpos = readb(&receive_pos);
uint_fast8_t needcopy = rpos - len;
if (needcopy) {
memmove(&receive_buf[copied], &receive_buf[copied + len]
, needcopy - copied);
copied = needcopy;
sched_wake_task(&t5uid1_wake);
}
irqstatus_t flag = irq_save();
if (rpos != readb(&receive_pos)) {
// Raced with irq handler - retry
irq_restore(flag);
continue;
}
receive_pos = needcopy;
irq_restore(flag);
break;
}
}
// Find the next command
int_fast8_t
t5uid1_find_command(uint8_t *buf, uint_fast8_t buf_len, uint_fast8_t *pop_count)
{
if (buf_len < T5UID1_HEADER_LEN)
goto need_more_data;
if (buf[0] != T5UID1_HEADER1 || buf[1] != T5UID1_HEADER2)
goto error;
uint_fast8_t cmdlen = buf[2];
if (cmdlen < 2)
goto error;
if (buf_len - T5UID1_HEADER_LEN < cmdlen)
goto need_more_data;
*pop_count = T5UID1_HEADER_LEN + cmdlen;
return 1;
need_more_data:
*pop_count = 0;
return 0;
error:
*pop_count = 1;
return -1;
}
// Process any incoming commands
void
t5uid1_task(void)
{
if (!sched_check_wake(&t5uid1_wake))
return;
uint_fast8_t rpos = readb(&receive_pos), pop_count;
int_fast8_t ret = t5uid1_find_command(receive_buf, rpos, &pop_count);
if (ret > 0) {
uint_fast8_t command = receive_buf[T5UID1_HEADER_LEN];
uint_fast8_t data_len = pop_count - T5UID1_HEADER_LEN - 1;
uint8_t *data = &receive_buf[T5UID1_HEADER_LEN + 1];
sendf("t5uid1_received command=%c data=%*s", command, data_len, data);
}
if (ret) {
t5uid1_pop_input(pop_count);
}
}
DECL_TASK(t5uid1_task);

14
src/generic/t5uid1_irq.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __GENERIC_T5UID1_IRQ_H
#define __GENERIC_T5UID1_IRQ_H
#include <stdint.h> // uint32_t
void t5uid1_init(uint32_t baud);
// callback provided by board specific code
void t5uid1_enable_tx_irq(void);
// t5uid1_irq.c
void t5uid1_rx_byte(uint_fast8_t data);
int t5uid1_get_tx_byte(uint8_t *pdata);
#endif // t5uid1_irq.h

View File

@ -68,4 +68,9 @@ config SERIAL
bool bool
default y default y
config T5UID1_SERIAL
depends on USBSERIAL
bool "Use UART0 for DGUS T5UID1 screen"
default n
endif endif

View File

@ -22,6 +22,8 @@ src-$(CONFIG_HAVE_GPIO_SPI) += lpc176x/spi.c
src-$(CONFIG_USBSERIAL) += lpc176x/usbserial.c lpc176x/chipid.c src-$(CONFIG_USBSERIAL) += lpc176x/usbserial.c lpc176x/chipid.c
src-$(CONFIG_USBSERIAL) += generic/usb_cdc.c src-$(CONFIG_USBSERIAL) += generic/usb_cdc.c
src-$(CONFIG_SERIAL) += lpc176x/serial.c generic/serial_irq.c src-$(CONFIG_SERIAL) += lpc176x/serial.c generic/serial_irq.c
src-$(CONFIG_T5UID1_SERIAL) += lpc176x/serial_t5uid1.c
src-$(CONFIG_T5UID1_SERIAL) += generic/t5uid1_irq.c
# Build the additional bin output file # Build the additional bin output file
target-y += $(OUT)klipper.bin target-y += $(OUT)klipper.bin

View File

@ -0,0 +1,80 @@
// lpc176x serial port
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/armcm_boot.h" // armcm_enable_irq
#include "board/irq.h" // irq_save
#include "board/t5uid1_irq.h" // t5uid1_rx_byte
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h" // gpio_peripheral
#include "sched.h" // DECL_INIT
// Write tx bytes to the serial port
static void
kick_tx(void)
{
for (;;) {
if (!(LPC_UART0->LSR & (1<<5))) {
// Output fifo full - enable tx irq
LPC_UART0->IER = 0x03;
break;
}
uint8_t data;
int ret = t5uid1_get_tx_byte(&data);
if (ret) {
// No more data to send - disable tx irq
LPC_UART0->IER = 0x01;
break;
}
LPC_UART0->THR = data;
}
}
void
UART0_IRQHandler(void)
{
uint32_t iir = LPC_UART0->IIR, status = iir & 0x0f;
if (status == 0x04)
t5uid1_rx_byte(LPC_UART0->RBR);
else if (status == 0x02)
kick_tx();
}
void
t5uid1_enable_tx_irq(void)
{
if (LPC_UART0->LSR & (1<<5)) {
irqstatus_t flag = irq_save();
kick_tx();
irq_restore(flag);
}
}
DECL_CONSTANT_STR("RESERVE_PINS_t5uid1", "P0.3,P0.2");
void
t5uid1_init(uint32_t baud)
{
// Setup baud
LPC_UART0->LCR = (1<<7); // set DLAB bit
enable_pclock(PCLK_UART0);
uint32_t pclk = SystemCoreClock;
uint32_t div = pclk / (baud * 16);
LPC_UART0->DLL = div & 0xff;
LPC_UART0->DLM = (div >> 8) & 0xff;
LPC_UART0->FDR = 0x10;
LPC_UART0->LCR = 3; // 8N1 ; clear DLAB bit
// Enable fifo
LPC_UART0->FCR = 0x01;
// Setup pins
gpio_peripheral(GPIO(0, 3), 1, 0);
gpio_peripheral(GPIO(0, 2), 1, 0);
// Enable receive irq
armcm_enable_irq(UART0_IRQHandler, UART0_IRQn, 0);
LPC_UART0->IER = 0x01;
}