Qidi X-Pro - Octoprint connects but temperature from M105 incorrect

The temperature reported from a M105 is not showing the temperatures correctly. Here is an example.

M104 T0 S100
M104 T0 S110
M140 S55

Recv: ok T:110 /110 B:55 /55 T0:100 /55 T1:110 /100 @:12 B@:128

Here it is all over the place. You can see the B:55 /55 is correct then you get to T0:100 /55 which appears to be showing the /55 bed temp and then T1:110 /100 which is showing /100 the T0 temperature. Then if you look at the original T:110 /110 that appears to be showing the T1 set and temperature.

So the problem is I could right a plugin to sort out those fields but they change. Yea I am not kidding. They move around so let me show you another example from the exact same Qidi X-Pro

So here it is the same commands as before but now look.

Recv: ok T:100 /100 B:55 /55 T0:100 /55 T1:110 /100 @:7 B@:128

T:100 /100 T0:100 and the final /100 are all representing T0 and no SET temp representing T1 is shown at all.

Octoprint showing the temperatures shows T0 as Bed, T1 as T0 and Bed as Bed for set temperatures.

I have no idea how Qidi displays the correct temperature on the display unless it uses the sensors right from the hardware on the motherboard and no GCODE which is my guess.

Soooo...

Has anyone solved this issue as of yet? I have 2 Qidi X-Plus printers and those work perfect with the plugin that solves some of the other issues. With those printers I can print as normal and it just works.

So for the hardware that has the issue it is the Qidi X-Pro

M115 - CBD make it.Date:Jan 23 2019 Time:14:58:22

Same M115 for both Q-Plus printers but single extruder does not have the temperature issues the X-Pro does.

Any solutions out there as of yet?

Broken AF firmware. That ain't the only issue with it. First of all install the Fix CBD plugin from the repository. And then I'll need a full serial.log to try to make sense of this new derivative which apparently now even breaks temperature reporting.

Whoever CBD is, at this point I'd like to punch them. This firmware has caused SO much grief and support overhead now, it's not even funny anymore.

If you can figure out the pattern, then I think the plugin I wrote for https://community.octoprint.org/t/improperly-reporting-klipper-tool-temperature/20768/26 could be modified to work.

Since I don't own the hardware, the modifications would have to be a cooperative effort (if you can't do it yourself).

No matter what we do we won't be able to fix temperature reporting with the standard M105 command. What we will have to do is write some sort of a plugin that rewrites the M105 command to M4000 command which on the Qidi is a printer status command that returns the following...

Recv: ok B:23/0 E1:36/0 E2:1077/0 X:274.003 Y:204.004 Z:200.000 F:0/0 D:0/0/1 T:0

So this is even more of a joke...

But from this we can extrapolate the information we need.

B:23/0 (Bed)
E1:36/0 (Tool 0)
E2:1077/0 (Tool 1)

So to add insult to injury on a single extruder machine they still report E2:1077/0

I am capable of writing a plugin for this Qidi specific firmware and I can read through the source to figure this out or if someone wants to present an example of how I can make this work I would appreciate it.

Need to rewrite requests of M105 to M4000 then need to parse that string to get the temperatures from that and discard the rest of the useless data.

So basically what Qidi is doing is giving standards the finger and simply writing their own M codes to use for their printers which is just beyond lame.

Sophia (Qidi Support Email) said that the software engineer "promises" to fix M105 in the next firmware update.

So can a plugin accomplish this task in the mean time?

I don't mind writing a plugin but it is going to go beyond what your plugin is doing. On top of parsing the data correctly I need to be able to send M4000 out to the printer in place of M105 which is going to require what I really am not sure yet. I have not got in to the source or even read up if there is plugin documentation but that is my next step if someone else does not have a better solution.

Okay was able to easily write the plugin to set M105 to M4000 and tested it. Off to write a plugin to now parse the data correctly.

Okay so I can send M4000 properly. I can receive and parse that line in to the proper temperatures but is there a hook to feed those temperatures to octoprint?

So I can create a parsed_temperature array but how can I feed that back to octoprint and have it be used properly?

I can not simply use the octoprint.comm.protocol.temperatures.received hook as it is never processes because it never receives the temperatures the way it expects to receive them. When I use that hook that trigger never gets fired.

Or can I modify the way octoprint parses the temperatures to start with? Then I could simply do that and it should just work.

I'm guessing this would be classified as a giant kludge but what I would do is:

  1. Using the gcode hook you are using now, change M105 to M4000 followed by M105.
  2. Capture the data returned from the M4000 and save it (as a parsed_temperature array).
  3. Using the ...temperatures.received hook, substitute your good parse_temperature array for the bad one captured by the M105.

I'm sure @foosel would have a more elegant solution.

The problem is the temperature.received hook is never fired because there was never a valid temperature received to begin with?

So you are saying that the M105 commands are being sent to the printer, it is returning what looks like a temperature report, but OctoPrint fails to parse it at all so it never triggers the temperatures.received hook?

If that's so, we are definitely at @foosel's mercy :star_struck:

If I leave it at M105 it works fine but if I use M4000 it does not.

Problem with M105 T1 no matter what you do does not show set temperature.

I have a working plugin now. Will clean it up and post it.

I don't think you understood my "kludge". Instead of substituting "M4000" for "M105", substitute "M4000,M105" for "M105", i.e. add an M4000 before the M105 and send both. I'm pretty sure the documentation has an example on how to do that. The M4000 will return the data you need and the M105 will trigger the hook where you will substitute good data for bad.

If you did understand me, then I'm not sure I understand your responses :blush:

Here is the plugin that could be severely cleaned up and optimized I am sure. But it is fully functioning at least on my Qidi X-Pro. This plugin does require you to have the "CBD Firmware Plugin" also installed from the Octoprint Plugin Manager.

Just to clarify what this does. On the X-Pro printer the original Qidi Firmware is severely broken. So temperatures are not reported properly and prevents you from using Octoprint at least for me. Your mileage may vary but it works perfect for me.

Basically it is using a different GCODE that Qidi invented for themselves that does report the temperature properly and formats the output so that Octoprint receives the proper temperature for both extruders and the bed. This allows Octoprint to function properly.

This plugin also detects if you have two extruders and if you do not and only have one it does nothing which is preferred. So if you install this on a different Qidi or even a single extruder printer that is not a Qidi it will not do anything but simply return the original output.

# coding=utf-8

import octoprint.plugin
import re

class QidiDualExtruderTempFixPlugin(octoprint.plugin.OctoPrintPlugin):

    parse_m4000 = re.compile('B:(\d+)\/(\d+) E1:(\d+)\/(\d+) E2:(\d+)\/(\d+)')

    # Credit to b-morgan for this rewrite routine I borrowed and modified for this purpose
    def rewrite_m105(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
        if self._printer_profile_manager.get_current_or_default()["extruder"]["count"] > 1 and gcode and gcode == "M105":
            return "M4000"
        return cmd

    def check_for_m4000(self, comm_instance, line, *args, **kargs):
        # Check to see if we received the M4000 response and if so extract temperature for octoprint
        if "B:" not in line:
            return line
        self._logger.debug("Original M4000 line %s" % line)
        m = self.parse_m4000.search(line)
        if self._printer_profile_manager.get_current_or_default()["extruder"]["count"] > 1:
            new_line = ("ok T:%s /%s B:%s /%s T0:%s /%s T1:%s %s" % (m.group(3), m.group(4), m.group(1), m.group(2), m.group(3), m.group(4), m.group(5), m.group(6)))
        else:
            new_line = ("ok T:%s /%s B:%s /%s" % (m.group(3), m.group(4), m.group(1), m.group(2)))
        self._logger.debug("Modified M4000 line %s" % new_line)
        return new_line

__plugin_name__ = "Qidi Dual Extruder Temperature Fix"
__plugin_description__ = "This plugin changes the way Octoprint queries the temperature on Qidi dual extruder printers this is due to Qidi using a very broken firmware you will also need the Fix CBD Firmware Plugin"
__plugin_author__ = "Robert Mann"
__plugin_license__ = "AGPLv3"
__plugin_pythoncompat__ = ">=2.7,<4"
def __plugin_load__():
    global __plugin_implementation__
    __plugin_implementation__ = QidiDualExtruderTempFixPlugin()

    global __plugin_hooks__
    __plugin_hooks__ = {
        "octoprint.comm.protocol.gcode.queuing": __plugin_implementation__.rewrite_m105,
        "octoprint.comm.protocol.gcode.received": __plugin_implementation__.check_for_m4000
    }

1 Like

Convert the response into a proper response to m105. OctoPrint will parse that, no need to parse temperatures manually and feeding them back or anything, just rewrite the return.

Or, you know, flash a proper firmware that isn't going above and beyond to screw you over with interoperability issues with established industry standards.

Is there such a firmware that will work on these. That is the question.

Still have a little weirdness going on during the wait commands. But all in all it works now at least.

When I get the M4000 results that is exactly what I am doing. Simply rewriting them to what the M105 SHOULD look like and Octoprint is happy to accept that and it just works.

Oh sorry, I just saw that I got pinged and totally missed that you had solved it :sweat_smile: