Snapmaker J1 Controller Firmware Open Source Release
We are pleased to announce that Snapmaker J1 controller firmware is now open source!
After nearly half a year of hard work, we have identified and fixed some known nasty bugs and J1 controller firmware is basically stable now. And we will continue to optimize its firmware and develop more features to improve print quality, ease your use, and help you create beautiful prints. To give back to the open source community, involve more people to solve problems, and eventually make J1 an even better 3D printer together, we decided to share the source code of the J1 controller. We can’t wait to see talented users from the community contributing to this project. As you might know, the firmware development of J1 is based on Marlin. Here, we sincerely thank all the contributors to Marlin.
Based on Marlin, we have made not a few changes, including but not limited to:
- First, Vibration Compensation. On an MCU platform with insufficient memory and limited performance, we implemented input shaping and precise sending of step pulses that are seen in Klipper firmware, and it supports up to eight input shapers. This solution makes it possible to realize high-speed, high-quality printing on many low-cost printers on the market. As we did in J1, through the optimization of input shaping and precise pulse control, we increased the maximum printer motion speed from 150mm/s to 350mm/s and the maximum acceleration from 3000mm/s^2 to 10000mm/s^2, while at the same time maintaining the accuracy and stability of motion mechanism.
- Second, Intuitive Touchscreen Interaction. Different from Snapmaker 2.0, we adopted a new communication protocol, rendering the communication between the touchscreen and controller more reliable and user-friendly.
- Third, Innovative IDEX Calibration Method. By utilizing electrical conduction, it enables users to complete the calibration in 10 minutes under the Assist Mode. It reduces errors and arrives at better accuracy.
Among the above improvements, the vibration compensation feature was actually not added to the picture at the very beginning. We took the risk of project delay and decided to implement this feature when the software development for J1 was almost done. Looking back, it did take us lots of hard work and resources to achieve this success. But we also found it immensely rewarding to realize vibration compensation in J1. Thanks to input shaping, J1 is able to achieve better motion performance on the Marlin firmware.
You might be interested in knowing why we added this feature near the end of the product development and how we did it. In the following, we will walk you through the development behind the scenes.
Just like all 3D printing enthusiasts, we have been following the open-source development in 3D printing. As you might know, Klipper incorporated vibration compensation for quite some time, and RRF (RepRapFirmware) also subsequently supported this feature. Because RRF is implemented on an MCU-based platform, we first integrated the input shaping and stepper control logic of RRF into Marlin and attempted to verify it on J1. However, the results were not as good as expected. After a detailed analysis of the code, we found that RRF does not perform input shaping on all movements. What it did was before the input shaping, it pre-determines whether the section of a movement meets the requirements for input shaping. If not, this section of the movement will follow the conventional trapezoidal motion profile to control the motor movement, which will lead to excessive vibration and layer shift when the print head runs in zigzags over short distances.
In terms of Klipper, it does not pre-determine whether a segment of a movement meets the requirements for input shaping. Klipper directly follows the theory of input shaping by convolving all the motions that the gcode inputs, thus achieving the result that movements of different features are shaped. However, there is no way for us to simply copy this processing logic of Klipper when only having one MCU since Klipper calculates the moment of step output by iteration, which requires a high-performance CPU. And, it also needs a large amount of memory to store signals of each step, which can not be achieved on our MCU. We thought the project might fail after some attempts, but there was still time left for us.
Thereafter, our engineers studied the paper on input shaping in-depth and looked for ways to implement it on an MCU platform.
Eventually, we found a feasible solution for MCU. Similar to the motion profile of RRF: through analytic expression, we obtain the piecewise functions S=f(t) for the motion segments with different accelerations, which are functions of displacement with respect to time. These piecewise functions can be used in stepper ISR to obtain the moment when the step signal should be emitted in the corresponding motion segment, and thus we can know the time interval of the stepper ISR interruptions. If the specified axis does not need to be shaped (e.g. Z axis), then it directly splits the original motion into motion segments of different accelerations and obtains its piecewise function queue. If the specified axis needs to be shaped (e.g. X&Y axis), then after splitting their original motion into motion segments of different accelerations, these segments are input to the shaper, which convolves the queue of original motion segments and outputs the piecewise function queue. The following is a simple step-by-step description of the input shaping process. If you want to get down to the details, you can refer to the codes in the GitHub repository. Let’s start with motion segments with different accelerations:
1. First of all, the motion block queue in Marlin is broken down into a move queue, where each move represents a motion segment with the same acceleration, and its function is S=f(t) = S0 + V0t0 + 0.5 * a * t^2. Then the whole move queue is actually a series of S=f(t) functions, except that the domain of each function is finite. And because each move in the queue moves consecutively on the same time axis (the end time of each move is the start time of the next move), the domain of this series of functions is also connected back and forth on the time axis.
2. Next, perform input shaping on the original move queue. The position of the shaper output is calculated by convolving the positions in the original move.
- First, a “shaper window” is created, which is a virtual time window on the time axis of the move queue. The window contains sampling points, each containing two important parameters: weight A and time T. The number of sampling points and weight A are determined by the input shaper configuration parameters. T is the time of the sampling point on the time axis of the move queue. And the time interval between each sampling point is also determined by the input shaper configuration parameters, so the window contains a fixed width of time. In addition, the “shaper window” moves from left to right on the time axis, and the right side is the direction of time growth.
- When the shaper window coincides with the time intervals of one or several moves on the time axis, the T of each sampling point falls within the time interval of a specific move. Then, the position of each sampling point corresponding to T can be calculated from the describing function S=f(t) of the move based on where T is located. The position of the original motion corresponding to T at each sampling point is weighted and summed using A corresponding to the sampling point to obtain the position after input shaping. The displacement function after input shaping can be expressed as: S’ = f'(t) = Σ [ Ai * Si ] = Σ { Ai * [S0 + V0 * Ti + 0.5 * ai * Ti^2] }.
- In this equation, Ai represents the weight of the ith sampling point, Ti represents the T corresponding to the ith sampling point, and ai represents the acceleration of the move corresponding to T.
3. When the window moving on the time axis of the move queue, the T of the sampling point corresponding to the move will keep changing, which causes the displacement function corresponding to the position of the sampling point to change as well, and we will get a series of S’. This series of S’ is the queue of piecewise functions after the convolution described earlier in this article. Now, all we have to do is to recompute the coefficients of each variable in S’ whenever the move corresponding to T changes at any sampling point in the window. We know from the previous step that S’ is actually also a function of displacement with respect to time t. If we have a known S’, we can likewise calculate the corresponding t by S’=f'(t), which is the next step to be done in stepper ISR.
4. Each time entering stepper’s ISR, we first send the step signal planned last time (if it exists). Then we use the piecewise function S'(t) for each axis to obtain the moment sending out the next step, because we only need to add one step to the current position and then insert it into the function to calculate t. Then we pick the minimum t of all the axes and configure the interval between the minimum and the current moment into the stepper’s timer, so that we can send the step signal precisely according to the planned time. We can then send out the step signal precisely at the planned time (due to limited performance, we can’t be absolutely precise on this, but that’s our goal).
The above is a brief introduction to the vibration compensation feature. If you are interested in the details, you can refer to the code in the repository. Basically, it contains two important parts; one is input shaping, which is the key to cancel vibration; the other one is to send the step signal at a precise moment.
It is very important to send the step signal at a precise time. Learning from our previous tests verifying RRF, we already knew that RRF’s step signal control is very different from that of Marlin: RRF strives to send the step signal for each axis exactly at the moment calculated by the piecewise function so that the motion of the toolhead can follow the path described by gcode as perfectly as possible. Klipper is also the same in this respect.
After deciding on the solution, we quickly verified the prototype and tested it thoroughly. The test results were exciting: a J1 with vibration compensation implemented only on the MCU platform was basically comparable with a J1 with Klipper control (Linux host + MCU). Therefore, we were able to add the vibration compensation feature to J1 right before the product launch. It wasn’t an easy task. But we decided to go all out simply because we want to keep making something wonderful!
GitHub Repository of J1 Controller Firmware: https://github.com/Snapmaker/SnapmakerController-IDEX
Learn more about Snapmaker J1.