OctoPrint / OctoPrint

OctoPrint is the snappy web interface for your 3D printer!

Home Page:https://octoprint.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Temperature misreported when T1 active, dual extruders, Marlin 2.0.9.2 based firmware on Sovol SV04

tringenbach opened this issue · comments

The problem

For my printer and it's latest vender firmware, when the second extruder is active, the temperature of the first extruder (T0) is misreported in Octoprint as the temperature of the second extruder (T1). This seems to be because Marlin's status line is also very confusing (and also wrong?).

I have a Sovol SV04 IDEX printer, that is running Sovol's modified firmware based off of Marlin 2.0.9.2. I've looked through their modifications, and maybe I missed something, but I don't think they come into play for this bug. I haven't checked if it happens in other versions of Marlin.

The temperature autoreport comes back like this:

 Recv:  T0:220.36 /220.00 B:60.03 /60.00 T0:50.44 /0.00 T1:220.36 /220.00 @:57 B@:37 @0:0 @1:57

Notice that T0 is listed twice!

The first T0 seems to actually be the active extruder. So this issue only happens after issuing a T1 command. At that point, the first T0 is actually T1.

So to report the temperature correctly when T1 is active, Octoprint needs to ignore the first T0 (which is really T1) and use the second one. Unfortunately for me, there's a check in the parser to avoid overriding a sensor once its value is seen.

I only recently started using Octoprint, so I haven't tried older versions, but I see that in 2ecc1c9 this was added:

diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py
index 9677002ae..d06b9529b 100644
--- a/src/octoprint/util/comm.py
+++ b/src/octoprint/util/comm.py
@@ -6268,6 +6268,10 @@ def parse_temperature_line(line, current):
     for match in re.finditer(regex_temp, line):
         values = match.groupdict()
         sensor = values["sensor"]
+        if sensor in result:
+            # sensor already seen, let's not overwrite stuff
+            continue
+

I assume that was added for a good reason and I shouldn't just submit a PR removing it; that would break something else.

Perhaps the correct fix is to special case the first T0 (or the second T0) and let it overwrite, but not anything else. But I don't have the breath of 3d printer experience necessary to say for sure.

Did the issue persist even in safe mode?

Yes, it did persist

If you could not test in safe mode, please state why ("currently printing" is NOT an excuse!)

No response

Version of OctoPrint

1.9.2

Operating system running OctoPrint

OctoPi Build 2023.07.20.144556, based on OctoPi 1.0.0, running on Raspberry Pi 4 Model B Rev 1.5

Printer model & used firmware incl. version

Sovol SV04, Sovol's version of Marlin 2.0.9.2

Browser and version of browser, operating system running browser

Chrome 116, MacOS

Checklist of files to include below

  • Systeminfo Bundle (always include!)
  • Contents of the JavaScript browser console (always include in cases of issues with the user interface)
  • Screenshots and/or videos showing the problem (always include in case of issues with the user interface)
  • GCODE file with which to reproduce (always include in case of issues with GCODE analysis or printing behaviour)

Additional information & file uploads

octoprint-systeminfo-20231001095603.zip

Recv:  T0:26.22 /0.00 B:42.48 /0.00 T0:26.22 /0.00 T1:34.71 /0.00 @:0 B@:0 @0:0 @1:0
[...]
Recv: echo:Active Extruder: 1
Recv: ok
[...]
Recv:  T0:34.61 /0.00 B:42.46 /0.00 T0:26.14 /0.00 T1:34.61 /0.00 @:0 B@:0 @0:0 @1:0
image

Hey there...

first of all thank you for that thorough report and analysis.

You are right that this looks indeed like the firmware is reporting T1 as T0 here after switching the active extruder. That however is something I'd consider a firmware bug. The whole temperature parsing is a horrible mess since a ton of firmware variants out there do their reports ever so slightly differently, however, so far an agreed upon behaviour seems to have been that in case of multiple extruders, T0 is always the first, T1 is always the second and so on. If no T0 is provided but rather a T, this is actually T0 (Smoothieware) and if T and T0, T1, etc are present, T is the temperature of the currently selected extruder.

See also the canonicalization logic that's been extracted from firmwares that OctoPrint has had to interface over the past decade:

def canonicalize_temperatures(parsed, current):
"""
Canonicalizes the temperatures provided in parsed.
Will make sure that returned result only contains extruder keys
like Tn, so always qualified with a tool number.
The algorithm for cleaning up the parsed keys is the following:
* If ``T`` is not included with the reported extruders, return
* If more than just ``T`` is reported:
* If both ``T`` and ``T0`` are reported, remove ``T`` from
the result.
* Else set ``T0`` to ``T`` and delete ``T`` (Smoothie extra).
* If only ``T`` is reported, set ``Tc`` to ``T`` and delete ``T``
* return
Arguments:
parsed (dict): the parsed temperatures (mapping tool => (actual, target))
to canonicalize
current (int): the current active extruder
Returns:
dict: the canonicalized version of ``parsed``
"""
reported_extruders = list(filter(lambda x: x.startswith("T"), parsed.keys()))
if "T" not in reported_extruders:
# Our reported_extruders are either empty or consist purely
# of Tn keys, no need for any action
return parsed
current_tool_key = "T%d" % current
result = dict(parsed)
if len(reported_extruders) > 1:
if "T0" in reported_extruders:
# Both T and T0 are present, let's check if Tc is too.
# If it is, we just throw away T (it's redundant). It
# it isn't, we first copy T to Tc, then throw T away.
#
# The easier construct would be to always overwrite Tc
# with T and throw away T, but that assumes that if
# both are present, T has the same value as Tc. That
# might not necessarily be the case (weird firmware)
# so we err on the side of caution here and trust Tc
# over T.
if current_tool_key not in reported_extruders:
# T and T0 are present, but Tc is missing - copy
# T to Tc
result[current_tool_key] = result["T"]
# throw away T, it's redundant (now)
del result["T"]
else:
# So T is there, but T0 isn't. That looks like Smoothieware which
# always reports the first extruder T0 as T:
#
# T:<T0> T1:<T1> T2:<T2> ... B:<B>
#
# becomes
#
# T0:<T0> T1:<T1> T2:<T2> ... B:<B>
result["T0"] = result["T"]
del result["T"]
else:
# We only have T. That can mean two things:
#
# * we only have one extruder at all, or
# * we are currently parsing a response to M109/M190, which on
# some firmwares doesn't report the full M105 output while
# waiting for the target temperature to be reached but only
# reports the current tool and bed
#
# In both cases it is however safe to just move our T over
# to Tc in the parsed data, current should always stay
# 0 for single extruder printers. E.g. for current_tool == 1:
#
# T:<T1>
#
# becomes
#
# T1:<T1>
result[current_tool_key] = result["T"]
del result["T"]
return result

Multiple entries for one key (two T0 as seen here) is something that's completely new and frankly something I'd like to declare a firmware bug. T and T0 would be something that has been already established as behaviour in the wild and is something that OctoPrint would handle correctly here, but T0 and T0 is just plain wrong in my opinion, and I'm not going to implement support for that (figuring out which one is the actual T0 feels like guesswork anyhow tbh).

In general we've seen firmware reporting weird stuff at the end of temperature lines that could be repetitions of existing entries, and which have proven to be non-sense with regards to the function of the temperature report, so those lines you mentioned up there are in place to just ignore that stuff and keep it from messing with temperature visualization on affected firmware.

Long story short, this is a firmware issue and something I'd rather not add yet another exception for in the temperature canonicalization code, which is also way more complicated than it has any right to be, but had to be thanks to the mess that is 3d printer firmwares out there. It would however be possible to work around this with a simple plugin utilizing the octoprint.comm.protocol.gcode.received hook until the firware in question is fixed.

@foosel Thanks for your reply.

I wanted to ask if it would make a difference how long the bug was present in Marlin. I suspected it was still present, but that is not the case.

But I'll post this anyway, maybe I can talk you into changing your mind since the fix is slightly different now that I have more info, but if not, maybe someone else will find this info and it will help them.

I did some digging with git annotate and friends, and found out that it was fixed.

MarlinFirmware/Marlin#23505

Looks like the first release the fix was included in was https://github.com/MarlinFirmware/Marlin/releases/tag/2.0.9.4
The bug report says it was introduced in 2.0.9.2, but I haven't confirmed that: https://github.com/MarlinFirmware/Marlin/releases/tag/2.0.9.2 (I know it's i 2.0.9.2, that's what I'm running, but I'm taking them at their word that it wasn't earlier.)

That means it was present in Marlin from October 2021 until June 2022.

And I know it's in the latest Sovol patched version of Marlin for their Sovol SV04, which is based on Marlin 2.0.9.2.

After reading the PR, I see that a possible work-around, if you were to change your mind and accept one, would be to notice there's two T0s and no T.

I'm working on upgrading my firmware to the latest Marlin. (Someone else has already done that, but they included some other changes, I'm going to try to make it work with the stock touch screen firmware...) So if I get that one, I won't need this work around.