From 9394bf592c4e60e9bd0645ebee89c7e5c474981c Mon Sep 17 00:00:00 2001 From: Arksine Date: Mon, 26 Oct 2020 21:25:20 -0400 Subject: [PATCH] docs: Add documentation for Klipper Printer Objects This documents the most useful printer objects available for query, providing the fields that a query returns and what the represent. Also add a basic "print state monitoring" example and "bed_mesh" example to web_api.md. Signed-off-by: Eric Callahan --- docs/printer_objects.md | 345 ++++++++++++++++++++++++++++++++++++++++ docs/web_api.md | 149 +++++++++++++++-- 2 files changed, 479 insertions(+), 15 deletions(-) create mode 100644 docs/printer_objects.md diff --git a/docs/printer_objects.md b/docs/printer_objects.md new file mode 100644 index 0000000..14f205a --- /dev/null +++ b/docs/printer_objects.md @@ -0,0 +1,345 @@ +As mentioned in the API documentation, it is possible to +[query](web_api.md#query-printer-object-status) or +[subscribe](web_api.md#subscribe-to-printer-object-status) +to "Klipper Printer Objects." These APIs are used to retreive state from +the Klippy Host. There are numerous printer objects in Klipper, many of +which are optional and only report status if they are enabled by Klipper's +configuration. Client's may retreive a list of active printer objects +that may report status via the +[list objects endpoint](web_api.md#list-available-printer-objects). This +should be done after Klipper reports its state as "ready". + +This section will provide an overview of the most useful printer objects. +If a developer is interested in retreiving state for an object listed here, +look in Klipper's source code for module you wish to query. If the module +contains a "get_status()" method, its return value will contain a dictionary +that reports state which can be queried. + +## webhooks +```json +{ + state: "startup", + state_message: "message" +} +``` +The `webhooks` object contains the current printer state and the current +state message. These fields match those returned via the `/printer/info` +endpoint. This is provided as a convience, clients may subscribe to `webhooks` +so they are asynchonously notified of a change to printer state. The `state` +may be "startup", "ready", "shutdown", or "error". The `state_message` +contains a message specific to the current printers state. + +## gcode_move +```json +{ + speed_factor: 1.0, + speed: 100.0, + extrude_factor: 1.0, + absolute_coordinates: true, + absolute_extrude: false, + homing_origin: [0.0, 0.0, 0.0, 0.0], + position: [0.0, 0.0, 0.0, 0.0], + gcode_position: [0.0, 0.0, 0.0, 0.0] +} +``` +The `gcode_move` object reports the current gcode state: +- `speed_factor`: AKA "feedrate", this is the current speed multiplier +- `speed`: The current gcode speed in mm/s. +- `extrude_factor`: AKA "extrusion multiplier". +- `absolute_coorinates`: true if the machine axes are moved using + absolute coordinates, false if using relative coordinates. +- `absolute_extrude`: true if the extruder is moved using absolute + coordinates, false if using relative coordinates. +- `homing_origin`: [X, Y, Z, E] - returns the "gcode offset" applied to + each axis. For example, the "Z" axis can be checked to determine how + much offset has been applied via "babystepping". +- `position`: [X, Y, Z, E] - The internal gcode position, including + any offsets (gcode_offset, G92, etc) added to an axis. +- `gcode_position`: [X, Y, Z, E] - The current gcode position + sans any offsets applied. For X, Y, and Z, this should match + the most recent "G1" or "G0" processed assuming the machine is + using absolute coordinates. + +Note: The printer's actual movement may lag behind the reported positional +coordinates due to lookahead. + +## toolhead +```json +{ + homed_axes: "xyz", + print_time: 0.0, + estimated_print_time: 0.0, + extruder: "extruder", + position: [0.0, 0.0, 0.0, 0.0], + max_velocity: 500.0, + max_accel: 3000.0, + max_accel_to_decel: 1500.0, + square_corner_velocity: 5.0 +} +``` +The `toolhead` object reports state of the current tool: +- `homed_axes`: a string containing the axes that are homed. If no axes + are homed, returns a null string. +- `print_time`: internal value, not generally useful for clients +- `estimated_print_time`: internal value, not generally useful for clients. +- `extruder`: the name of the currently selected extruder, ie "extruder" + or "extruder1". +- `position`: [X, Y, Z, E] - This the the last position toward which the tool + was commanded to move. It includes any offsets applied via gcode as well + as any transforms made by modules such as "bed_mesh", "bed_tilt", or + "skew_correction". +- `max_velocity`: The currently set maximum velocity of the tool (mm/s^2). +- `max_accel`: The currently set maximum acceleration of the tool (mm/s^2). +- `max_accel_to_decel`: The currently set maximum accel to decel of the tool. + This value is the maximum rate at which the tool can transition from + acceleration to deceleration (mm/s^2). +- `square_corner_velocity`: The currently set square corner velocity. This + is the maximum velocity at which the tool may travel a 90 degree corner. + +Note: `max_velocity`, `max_accel`, `max_accel_to_decel`, and +`square_corner_velocity` can be changed by the `SET_VELOCITY_LIMIT` gcode. +`M204` can also change `max_accel`. + +## configfile +```json +{ + config: {}, + save_config_pending: false +} +``` +The `configfile` object reports printer configuration state: +- `config`: This is an object containing the configuration as read from + printer.cfg. Each config section will be an object containing the + configured options. Option values will ALWAYS be reported as + strings. Note that default values are not reported, only options + configured in printer.cfg are present. +- `save_config_pending`: True if the printer has taken an action which + has updated the internal configuration (ie: PID calibration, probe + calibration, bed mesh calibration). This allows clients to present + the user with the option to execute a SAVE_CONFIG gcode which will + save the configuration to printer.cfg and restart the Klippy Host. + +## extruder +*Enabled when [extruder] is included in printer.cfg* +Note: If multiple extruders are configured, extruder 0 is available as +`extruder`, extruder 1 as `extruder1` and so on. +```json +{ + temperature: 0.0, + target: 0.0 + pressure_advance: 0.0, + smooth_time: 0.0 +} +``` +The `extruder` object reports state of an extruder: +- `temperature`: The extruder's current temperature (in C). +- `target`: The extruder's target temperature (in C). +- `pressure_advance`: The extruder's current pressure advance value. +- `smooth_time`: The currently set time range to use when calculating the + average extruder velocity for pressure advance. + +## heater_bed +*Enabled when [heater_bed] is included in printer.cfg* +```json +{ + temperature: 0.0, + target: 0.0 +} +``` +The `heater_bed` object reports state of the heated bed: +- `temperature`: The bed's current temperature +- `target`: The bed's target temperature + +## fan +*Enabled when [fan] is included in printer.cfg* +```json +{ + speed: 0.0 +} +``` +The `fan` object returns state of the part cooling fan: +- `speed`: The current fan speed. This is reported as a + percentage of maximum speed in the range of 0.0 - 1.0. + +## idle_timeout +```json +{ + state: "Idle", + printing_time: 0.0 +} +``` + +The `idle_timeout` object reports the idle state of the printer: +- `state`: Can be "Idle", "Ready", or "Printing". The printer will + transition to the "Printing" state whenever a gcode is issued that + commands the tool, this includes manual commands. Thus this should + not be used to determine if a gcode file print is in progress. It can + however be used to determine if the printer is busy. +- `printing_time`: The amount of time the printer has been in the + "Printing" state. This is reset to 0 whenever the printer transitions + from "Printing" to "Ready". + +## virtual_sdcard +*Enabled when [virtual_sdcard] is included in printer.cfg* +```json +{ + progress: 0.0, + is_active: false, + file_position: 0 +} +``` +The `virtual_sdcard` object reports the state of the virtual sdcard: +- `progress`: The print progress reported as a percentage of the file + read, in the range of 0.0 - 1.0. +- `is_active`: Returns true if the virtual sdcard is currently processing + a file. Note that this will return false if a virtual sdcard print is + paused. +- `file_position`: The current file position in bytes. This will always + be an integer value + +Note: `progress` and `file_position` will persist after a print has +paused, completed, or errored. They are cleared when the user issues +a SDCARD_RESET_FILE gcode or when a new print has started. + +## print_stats +*Enabled when [virtual_sdcard] is included in printer.cfg* +```json +{ + filename: "", + total_duration: 0.0, + print_duration: 0.0, + filament_used: 0.0, + state: "standby", + message: "" +} +``` +The `print_stats` object reports "virtual_sdcard" print state: +- `filename`: The name of the current file loaded. This will be a null + string if no file is loaded. Note that name is a path relative to the + gcode folder, thus if the file is located in a subdirectory it would + be reported as "my_sub_dir/myprint.gcode". +- `total_duration`: The total time (in seconds) elapsed since a print + has started. This includes time spent paused. +- `print_duration`: The total time spent printing (in seconds). This is + equivalent to `total_duration` - time paused. +- `filament_used`: The amount of filament used during the current print + (in mm). Any extrusion during a pause is excluded. +- `state`: Current print state. Can be "standby", "printing", "paused", + "complete", or "error". If an error is detected the print will abort. +- `message`: If an error is detected, this field contains the error + message generated. Otherwise it will be a null string. + +Note: After a print has started all of the values above will persist until +the user issues a SDCARD_RESET_FILE gcode or when a new print has started. + +## display_status +*Enabled when [display] or [display_status] is included in printer.cfg* +```json +{ + message: "", + progress: 0.0 +} +``` +The `display_status` object contains state typically used to update displays: +- `message`: The message set by a M117 gcode. If no message is set this will + be a null string. +- `progress`: The percentage of print progress, as reported by M73. This + will be in the range of 0.0 - 1.0. If no M73 has been issued this value + will fallback to the eqivalent of `virtual_sdcard.progess`. Note that + progress updated via M73 has a timeout. If no M73 is received after 5 + seconds, `progress` will be set to the fallback value. + +## temperature_sensor sensor_name +*Enabled when [temperature_sensor sensor_name] is included in printer.cfg. It is* +*possible for multiple temperature sensors to be configured.* +```json +{ + temperature: 0.0, + measured_min_temp: 0.0, + measured_max_temp: 0.0 +} +``` +A `temperature_sensor` reports the following state: +- `temperature`: Sensor's current reported temperature +- `measured_min_temp`: The mimimum temperature read from the sensor +- `measured_max_temp`: The maximum temperature read from the sensor + +## temperature_fan fan_name +*Enabled when [temperature_fan fan_name] is included in printer.cfg. It is* +*possible for multiple temperature fans to be configured.* +```json +{ + speed: 0.0, + temperature: 0.0, + target: 0.0 +} +``` +A `temperature_fan` reports the following state: +- `speed`: Current fan speed as a percentage of maximum speed, reported + in the range of 0.0 - 1.0 +- `temperature`: Currently reported temperature of the sensor associated + with the fan +- `target`: The current target temperature for the `temperature_fan`. + +## filament_switch_sensor sensor_name +*Enabled when [filament_switch_sensor sensor_name] is included in printer.cfg.* +*It is possible for multiple filament sensors to be configured.* +```json +{ + filament_detected: false, + enabled: true +} +``` +A `filament_switch_sensor` reports the following state: +- `filament_detected`: Set to true if the switch detects filament, otherwise + false +- `enabled`: Set to true if the sensor is currently enabled, otherwise false + +## output_pin pin_name +*Enabled when [output_pin pin_name] is included in printer.cfg.* +*It is possible for multiple output pins to be configured.* +```json +{ + value: 0.0 +} +``` +An `output_pin` reports the following state: +- `value`: The currently set value of the pin, in the range of 0.0 - 1.0. + A digital pin will always be 0 or 1, whereas a pwm pin may report a value + across the entire range. + +## bed_mesh +*Enabled when [bed_mesh] is included in printer.cfg.* +```json +{ + profile_name: "", + mesh_min: [0.0, 0.0], + mesh_max: [0.0, 0.0], + probed_matrix: [[]], + mesh_matrix: [[]] +} +``` +The `bed_mesh` printer object reports the following state: +- `profile_name`: The name of the currently loaded profile. If no profile is + loaded then this will report a null string. If the user is not using bed_mesh + profile management then this will report "default" after mesh calibration + completes. +- `mesh_min`: [X, Y] - The minimum x and y coordinates of the mesh. +- `mesh_max`: [X, Y] - The maximum x and y coordinates of the mesh. +- `probed_matrix`: A 2 dimensional array representing the matrix of probed + values. If the matrix has not been probed the the result is `[[]]`. +- `mesh_matrix`: A 2 dimension array representing the interpolated mesh. If + no matrix has been generated the result is `[[]]`. + +Note: See [web_api.md](web_api.md##bed-mesh-coordinates) for an example +of how to use this information to generate (X,Y,Z) coordinates. + +## gcode_macro macro_name +*Enabled when [gcode_macro macro_name] is included in printer.cfg.* +*It is possible for multiple gcode macros to be configured.* + +Gcode macros will report the state of configured "variables". +While user defined macros likely won't report state that is useful +for a client, it is possible for client developers to recommend or +request a specific gcode_macro configuration, then have the client +take action based on the variables reported by the macro. \ No newline at end of file diff --git a/docs/web_api.md b/docs/web_api.md index b82bf65..009c08c 100644 --- a/docs/web_api.md +++ b/docs/web_api.md @@ -88,7 +88,7 @@ that uses promises to return responses and errors (see json-rcp.js). ## Printer Status -### Request available printer objects and their attributes: +### List available printer objects: - HTTP command:\ `GET /printer/objects/list` @@ -103,7 +103,7 @@ that uses promises to return responses and errors (see json-rcp.js). { objects: ["gcode", "toolhead", "bed_mesh", "configfile",....]} ``` -### Query the a status for an object, or group of objects: +### Query printer object status: - HTTP command:\ `GET /printer/objects/query?gcode` @@ -114,7 +114,7 @@ that uses promises to return responses and errors (see json-rcp.js). - Websocket command:\ `{jsonrpc: "2.0", method: "printer.objects.query", params: - {objects: {gcode: [], toolhead: ["position", "status"]}}, + {objects: {gcode: null, toolhead: ["position", "status"]}}, id: }` Note that an empty array will fetch all available attributes for its key. @@ -138,13 +138,16 @@ that uses promises to return responses and errors (see json-rcp.js). ...} } ``` -### Subscribe to a status request or a batch of status requests: +See [printer_objects.md](printer_objects.md) for details on the printer objects +available for query. + +### Subscribe to printer object status: - HTTP command:\ `POST /printer/objects/subscribe?gcode=gcode_position,bus&extruder=target` - Websocket command:\ `{jsonrpc: "2.0", method: "printer.objects.subscribe", params: - {objects: {gcode: [], toolhead: ["position", "status"]}}, + {objects: {gcode: null, toolhead: ["position", "status"]}}, id: }` - Returns:\ @@ -166,13 +169,16 @@ that uses promises to return responses and errors (see json-rcp.js). ...} } ``` - Note that Moonraker's current behavior is to maintain a superset of all - subscriptions, thus you may receive updates for objects that you did not - request. This behavior is subject to change in the future (where each - client receives only the subscriptions it requested). +See [printer_objects.md](printer_objects.md) for details on the printer objects +available for subscription. - Future updates for subscribed objects are sent asynchronously over the - websocket. See the `notify_status_update` notification for details. +Note that Moonraker's current behavior is to maintain a superset of all +subscriptions, thus you may receive updates for objects that you did not +request. This behavior is subject to change in the future (where each +client receives only the subscriptions it requested). + +Future updates for subscribed objects are sent asynchronously over the +websocket. See the `notify_status_update` notification for details. ### Query Endstops - HTTP command:\ @@ -865,7 +871,7 @@ Where `metadata` is an object in the following format: # Appendix -### Websocket setup +## Websocket setup All transmissions over the websocket are done via json using the JSON-RPC 2.0 protocol. While the websever expects a json encoded string, one limitation of Eventlet's websocket is that it can not send string encoded frames. Thus @@ -886,7 +892,7 @@ ws://host:port/websocket?token=<32 character base32 string> ``` This is necessary as it isn't currently possible to add `X-Api-Key` to a -websocket's request header. +Websocket object's request header. The following startup sequence is recommened for clients which make use of the websocket: @@ -904,8 +910,121 @@ the websocket: get a description of the error from the `state_message` attribute - If `state == "shutdown"` then Klippy is in a shutdown state. - If `state == "startup"` then re-request printer info in 2s. -- Repeat step 2s until Klipper reports ready. T +- Repeat step 2 until Klipper reports ready. - Client's should watch for the `notify_klippy_disconnected` event. If it reports disconnected then Klippy has either been stopped or restarted. In this instance the client should repeat the steps above to determine when - klippy is ready. \ No newline at end of file + klippy is ready. + +## Basic Print Status +An advanced client will likely use subscriptions and notifications +to interact with Moonraker, however simple clients such as home automation +software and embedded devices (ie: ESP32) may only wish to monitor the +status of a print. Below is a high level walkthrough for receiving print state +via polling. + +- Set up a timer to poll at the desired interval. Depending on your use + case, 1 to 2 seconds is recommended. +- On each cycle, issue the following request: + - `GET http://host/printer/objects/query?webhooks&virtual_sdcard&print_stats`\ + Or via json-rpc:\ + `{'jsonrpc': "2.0", 'method': "printer.objects.query", 'params': + {'objects': {'webhooks': null, 'virtual_sdcard': null, + 'print_stats': null}}, id: }` +- If the request returns an error or the returned `result.status` is an empty + object printer objects are not available for query. Each queried object + should be available in `result.status`. The client should check to make + sure that all objects are received before proceeding. +- Inspect `webhooks.ready`. If the value is not "ready" the printer + is not available. `webhooks.message` contains a message pertaining + to the current state. +- If the printer is ready, inspect `print_stats.state`. It may be one + of the following values: + - "standby": No print in progress + - "printing": The printer is currently printing + - "paused": A print in progress has been paused + - "error": The print exited with an error. `print_stats.message` + contains a related error message + - "complete": The last print has completed +- If `print_stats.state` is not "standby" then `print_stats.filename` + will report the name of the currently loaded file. +- `print_stats.filename` can be used to fetch file metadata. It + is only necessary to fetch metadata once per print.\ + `GET http://host/server/files/metadata?filename=`\ + Or via json-rpc:\ + `{jsonrpc: "2.0", method: "server.files.metadata", + params: {filename: "filename"} + , id: }`\ + If metadata extraction failed then this request will return an error. + Some metadata fields are only populated for specific slicers, and + unsupported slicers will only return the size and modifed date. +- There are multiple ways to calculate the ETA, this example will use + file progress, as it is possible calculate the ETA with or without + metadata. + - If `metadata.estimated_time` is available, the eta calculation can + be done as: + ```javascript + // assume "result" is the response from the status query + let vsd = result.status.virtual_sdcard; + let prog_time = vsd.progress * metadata.estimated_time; + let eta = metadata.estimated_time - prog_time + ``` + Alternatively, one can simply subtract the print duration from + the estimated time: + ```javascript + // assume "result" is the response from the status query + let pstats = result.status.print_status; + let eta = metadata.estimated_time - pstats.print_duration; + if (eta < 0) + eta = 0; + ``` + - If no metadata is available, print duration and progress can be used to + calculate the ETA: + ```javascript + // assume "result" is the response from the status query + let vsd = result.status.virtual_sdcard; + let pstats = result.status.print_stats; + let total_time = pstats.print_duration / vsd.progress; + let eta = total_time - pstats.print_duration; + ``` +- It is possible to query additional object if a client wishes to display + more information (ie: temperatures). See + [printer_objects.md](printer_objects.md) for more information. + +## Bed Mesh Coordinates +The [bed_mesh](printer_objects.md#bed_mesh) printer object may be used +to generate three dimensional coordinates of a probed area (or mesh). +Below is an example (in javascript) of how to transform the data received +from a bed_mesh object query into an array of 3D coordinates. +```javascript +// assume that we have executed an object query for bed_mesh and have the +// result. This example generates 3D coordinates for the probed matrix, +// however it would work with the mesh matrix as well +function process_mesh(result) { + let bed_mesh = result.status.bed_mesh + let matrix = bed_mesh.probed_matrix; + if (!(matrix instanceof Array) || matrix.length < 3 || + !(matrix[0] instanceof Array) || matrix[0].length < 3) + // make sure that the matrix is valid + return; + let coordinates = []; + let x_distance = (bed_mesh.mesh_max[0] - bed_mesh.mesh_min[0]) / + (matrix[0].length - 1); + let y_distance = (bed_mesh.mesh_max[1] - bed_mesh.mesh_min[1]) / + (matrix.length - 1); + let x_idx = 0; + let y_idx = 0; + for (const x_axis of matrix) { + x_idx = 0; + let y_coord = bed_mesh.mesh_min[1] + (y_idx * y_distance); + for (const z_coord of x_axis) { + let x_coord = bed_mesh.mesh_min[0] + (x_idx * x_distance); + x_idx++; + coordinates.push([x_coord, y_coord, z_coord]); + } + y_idx++; + } +} +// Use the array of coordinates visualize the probed area +// or mesh.. +```