-
Connecting it all together
01/24/2022 at 01:18 • 0 commentsThe TFT28 clone that the Artillery Sidewinder X1 uses requires +5v to operate. The original from MKS has an optional +12v input for power, but Artillery's clone does not. Normally, it gets the +5v from the controller's AUX port, but I need to disconnect that and redirect those serial pins from the TFT28 elsewhere. The good news is that on both ends (both the MKS Gen_L AUX port, and the connector on the TFT28), there are spare +5v and GND pins we can use. I wired up a couple of 1x4 dupont connectors with positive on one end, and GND immediate next to it, leaving the remaining two pins empty. I made this cable the same length as the existing cable between the controller and the TFT, and swapped them out. Here's a couple of pictures for orientation:
With power squared away, it was time to connect the serial cable I made earlier. I threaded it in through the hole in the back where the Y end stop enters the case, and routed it along with the rest of the cables. Once I got it to the TFT, I connected it to the other half of the connector that was previously connected to the AUX port.
When I went to plug it in, I found that I had TX and RX reversed from what I expected and had to swap them. When I pinned it out, the only doc I could find was the pinout for the AUX port, and I had based it on that, forgetting to invert TX and RX. Oops. Thankfully the PCB is labeled to the side of the connector.
Sadly, when I reassembled the printer and tested it all, I had no serial communication. The TFT powered up, but it wasn't updating the temperatures on the display or sending g-code to Klipper. I've got a couple guesses as to what is wrong:
- Baud rate - Klipper is operating at a different speed than the old Marlin firmware was, and the TFT firmware needs to be updated to match
- Despite all my planning, I still have TX and RX backwards. It has been my experience that you can plan all you want, it's still darned close to 50/50 that you'll get it right.
Ideas for troubleshooting:
- determine the default baud rate for the TFT and reconfigure Klipper to use that
- find another serial-TTL adapter and emulate the TFT, does the system even work? If so, emulate the other end, does the TFT work with the serial-TTL adapter? Probably should have checked that before I started >_>
-
udev rules
01/23/2022 at 22:02 • 0 commentsThis whole complicated system I'm about to try involved three USB serial ports. The two USB serial TTL adapters, and the one in the printer. Sure we could probably set everything up using the various
/dev/ttyUSB*
ports, but what happens when the printer gets turned off and back on? What happens when the RasPi restarts? This is where udev comes in handy.We can write some udev rules such that each port gets assigned an additional symlink of a name we chose, and we can use *those* names. So the printer is always
/dev/ttySidewinder
for instance.Unfortunately, the USB serial-TTL adapters I've got don't have unique serial numbers (I know this from previous udev experiments). This makes it rather difficult to tell them apart with a udev rule. The only way I could come up with was to specify which port they were connected to. This isn't great if everything gets disconnected and reconnected in a different order, as then it wont work right. But short of ordering more parts, this is the solution I've got.
The first order of business is to figure out which port is which. To do this, I made sure that the printer was the only USB serial device connected, and then plugged it into the port I wanted to use for the PC serial-TTL adapter. Then it was something like this:
$ ls /dev/ttyUSB* /dev/ttyUSB1
So it looks like
/dev/ttyUSB1
is our printer at the moment, we'll need that device name in our next command:$ udevadm info --name=/dev/ttyUSB1 --attribute-walk [lots and lots of output]
I got several screens of output from this. The bit we're most interested in, was (in my case) the fourth device from the top. The first one that has a
KERNELS==
that looks something like this one, but without a colon in it:looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5': KERNELS=="1-1.5"
That's going to be the bit I use to distinguish this port from another one. I copied that whole
KERNELS==
line and stuck it in a document with a note as to which port it was associated with.Then I repeated the steps for the USB port I wanted to use for the Printer serial-TTL adapter.
Once I had a way to distinguish the two USB ports we intend to use for the serial-TTL adapters, I needed to find out their details so I could actually write a udev rule. I moved the 3D printer's USB cable, and plugged in the two USB serial-TTL adapters, taking care to get them into the correct ports. If you saw their photo in the previous log, I had labeled them to make this step easier. I probably should have disconnected the 3D printer, but I hadn't. Thankfully it retained the same /dev/ttyUSB1 that it was using earlier.
I reran the udevadm info command again, this time on one of my new USB serial ports:
$ udevadm info --name=/dev/ttyUSB2 --attribute-walk Udevadm info starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device. looking at device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5:1.0/ttyUSB2/tty/ttyUSB2': KERNEL=="ttyUSB2" SUBSYSTEM=="tty" DRIVER=="" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5:1.0/ttyUSB2': KERNELS=="ttyUSB2" SUBSYSTEMS=="usb-serial" DRIVERS=="pl2303" ATTRS{port_number}=="0" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5:1.0': KERNELS=="1-1.5:1.0" SUBSYSTEMS=="usb" DRIVERS=="pl2303" ATTRS{bNumEndpoints}=="03" ATTRS{bInterfaceSubClass}=="00" ATTRS{authorized}=="1" ATTRS{bInterfaceClass}=="ff" ATTRS{bInterfaceProtocol}=="00" ATTRS{bInterfaceNumber}=="00" ATTRS{bAlternateSetting}==" 0" ATTRS{supports_autosuspend}=="1" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5': KERNELS=="1-1.5" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{bDeviceProtocol}=="00" ATTRS{devnum}=="8" ATTRS{idProduct}=="2303" ATTRS{rx_lanes}=="1" ATTRS{devpath}=="1.5" ATTRS{urbnum}=="26" ATTRS{speed}=="12" ATTRS{devspec}=="(null)" ATTRS{bNumConfigurations}=="1" ATTRS{bDeviceSubClass}=="00" ATTRS{avoid_reset_quirk}=="0" ATTRS{bDeviceClass}=="00" ATTRS{removable}=="removable" ATTRS{bMaxPacketSize0}=="64" ATTRS{authorized}=="1" ATTRS{bcdDevice}=="0300" ATTRS{product}=="USB-Serial Controller" ATTRS{ltm_capable}=="no" ATTRS{maxchild}=="0" ATTRS{bMaxPower}=="100mA" ATTRS{tx_lanes}=="1" ATTRS{manufacturer}=="Prolific Technology Inc." ATTRS{bNumInterfaces}==" 1" ATTRS{idVendor}=="067b" ATTRS{bmAttributes}=="a0" ATTRS{busnum}=="1" ATTRS{quirks}=="0x0" ATTRS{bConfigurationValue}=="1" ATTRS{version}==" 2.00" ATTRS{configuration}==""
I can already tell from the
KERNELS==1-1.5
that it's the one plugged into the USB port I set aside for the PC connection. The important bits I'm looking for however are theATTRS{idVendor}=="067b"
andATTRS{idProduct}=="2303"
. These combined with a few other bits will give us a rule that only applies to these specific devices, and theKERNELS==
bits we gather beforehand will let us specify which one is which based on which port they're in.Next thing I did was create the file
/etc/udev/rules.d/99-custom-serial.rules
:SUBSYSTEM=="tty",SUBSYSTEMS=="usb",DRIVERS=="usb",ATTRS{idVendor}=="067b",ATTRS{idProduct}=="2303",KERNELS=="1-1.5",SYMLINK+="ttyTFT28_PC" SUBSYSTEM=="tty",SUBSYSTEMS=="usb",DRIVERS=="usb",ATTRS{idVendor}=="067b",ATTRS{idProduct}=="2303",KERNELS=="1-1.4",SYMLINK+="ttyTFT28_Printer" SUBSYSTEM=="tty",SUBSYSTEMS=="usb",DRIVERS=="usb",ATTRS{idVendor}=="1a86",ATTRS{idProduct}=="7523",SYMLINK+="ttySidewinder"
SUBSYSTEM=="tty"
ensures we are working with a device that's handled by the bit that makes /dev/tty* devices.SUBSYSTEMS=="usb"
andDRIVERS=="usb"
limit us to USB parent devices.ATTRS{idVendor}
matches the USB vendor ID, andATTRS{idProduct}
matches the product ID, those two limit us to a specific model of device.KERNELS
matches to a specific port.SYMLINK+=
assigns an additional symlink, with the provided name, pointing back to the original device.In this case I made three devices:
ttyTFT28_PC USB serial-TTL adapter that OctoPrint will use ttyTFT28_Printer USB serial-TTL adapter that will be connected to /tmp/printer ttySidewinder USB connection to MKS Gen_L controller in Sidewinder X1 printer Of course, they don't exist *yet*, I need to tell udev to reprocess all the rules:
$ sudo udevadm control --reload-rules && sudo udevadm trigger
Did it work?
$ ls /dev/ttyS* /dev/ttyT* -l lrwxrwxrwx 1 root root 7 Jan 23 15:58 /dev/ttySidewinder -> ttyUSB1 lrwxrwxrwx 1 root root 7 Jan 23 15:58 /dev/ttyTFT28_PC -> ttyUSB2 lrwxrwxrwx 1 root root 7 Jan 23 15:58 /dev/ttyTFT28_Printer -> ttyUSB3
Woo! Next up, I need to connect
/dev/ttyTFT28_Printer
to/tmp/printer
. A systemd service seems like the way to go, something that can run alongside the Klipper service.I installed socat (the apt package name is
socat
:) ) After some experimenting I determined that A) Klipper doesn't use an systemd service, it used an old init.d script. And B) I couldn't for the life of me figure out how to get my new service to come back up when Klipper came back up. Which meant if Klipper was ever restarted from the OctoPrint GUI, there'd be a problem. The good news is that even though Klipper isn't using a systemd unit file, systemd still treats it as if it were. So I ended up making a drop-in file to start socat after Klipper starts:/etc/systemd/system/klipper.service.d/tft28.conf
[Unit] ExecStartPost=/usr/bin/socat /dev/ttyTFT28_Printer,raw,echo=0,crnl /tmp/printer,raw,echo=0,crnl
And then reloaded systemd and restarted Klipper:
$ sudo systemctl daemon-reload $ sudo systemctl restart klipper
I was then able to reconfigure OctoPrint to use
/dev/ttyTFT28_PC
and connect it, observe it happily polling the printer for the current temp, and receiving it back. So the serial communication was good between OctoPrint and Klipper, despite everything we added in between.The next step will be opening the printer and connecting the TFT, which ought to be a much shorter log than this one :)
-
Building the cable
01/23/2022 at 19:47 • 0 commentsI had a couple of USB serial TTL adapters with cables that had dupont connectors on the ends. These were great for using with microcontrollers or hacking around with embedded devices. They had a +5v (or +3.3v if you cracked them open and warmed up the soldering iron), which I didn't need for this, but they were what was on hand.
Everything is wired as per the diagram in the previous log. The far end has a dupont connector with the pins arranged to match the pinout of the TFT28. My best reference for that was the manual for the GEN_L, with the pinout for the AUX connector, so there's a slight chance it's mirrored, but once I get the printer open I can verify the pin markings on the actual TFT28. Thankfully they are printed right on the PCB. The connector in the middle of the cable is a JST-SM. I chose it both because I find them slightly easier to crimp than dupont, and because they lock together. The short jumper between the two USB serial-TTL adapters is just soldered.
On the short jumper I connected the shield of the cable on both ends to GND. On the long cable from the serial-TTL adapter, the shield is connected to GND. In the middle of the two long cables the shield is not connected, as I had already cut it off. Oops. To make up for this, the dupont end of the other long cable has the shield also connected to GND. I intentionally did not want the shield connecting both GNDs together, as I had some vague memory of ground loops or something.
The next step will be to install it, both on the RasPi and the printer, and then sort out the software side of the things. -
/tmp/printer
01/23/2022 at 19:17 • 0 commentsSince Klipper doesn't send G-code to the physical printer's serial port, it needs to receive it on a host machine for processing before forwarding on in it's preferred format. To do this, it creates a virtual serial port, typically named
/tmp/printer
. OctoPrint then connects to this serial port instead of the more-typical/dev/ttyUSBx
, etc.My plan is to use a pair of USB Serial-TTL adapters connected together, to provide a physical point I can connect the TFT28. OctoPrint will connect to one of them, and I'll use something like socat to connect the other to
/tmp/printer
. It should look something like this:I'm not 100% sure the AUX port on the GEN_L doesn't mirror both TX and RX from the main serial port (USB) to the TFT28's RX, if that's the case I'll have to come up with something different. Probably something smarter. But this is the current plan as it's easy to try with what I've got lying around.
Note: All my experience with Klipper is from the perspective of an OctoPrint user, I understand it can be used without it, but as I've not run it that way, I've probably mistakenly attributed some things to one that belong to the other.