Fighting with computers

Computers are not always friendly.

Wednesday, October 19, 2016

My Printrbot experience

While some friends were waiting for their first Printrbot off Kickstarter I had already built one with the parts Brook Drumm posted on Thingiverse. That was quite a while ago. It was a cute little machine that I sold to a fellow reprapper.

Last year, while I was working on a closed-loop DC motor controller for replacing the steppers of our 3D printers, Brook Drumm offered to help and sent me a free Printrbot Simple Metal, fully assembled to be used as a testbed printer for such type of motion control. I had a difficult time trying to convince the taxman that the printer was really a gift and eventually I had to gave in and pay some custom taxes although that was not really a product I was buying and the sender was charging me no money for that.

I was surprised of how compact the thing appeared and how smooth and solid all the axis moved. However, the 3d printer was not intended to be used as a 3d printer and the first thing I had to do was to partially disassemble a brand new unit and to start to adapt the other type of motor.

Adapting another motor while keeping the functionality of the printer was a dead end given my limited mechanical skills and how tight fit all the parts on this model of printer. I managed to get a motor working on Y axis but then I would lose Z-axis functionality as it was not possible to get both axis working with the type of motor I was using.

At then end, the mechanical limitations made the task to lay in my project's table for a long time without any new development. I want to stress that when I mentioned the problem I was having to Brook and the inability to get the test of operation he offered to send another model but I already felt embarrassed enough for not achieving the original goal to go on that path.

So one day I decided it was about time that I reassemble the Simple with its original motor and get it work as a regular 3d printer. Once it was up and running I did a sample printing while the printer sat on a stool in my office and I left the printer unattended for a coffee break with some colleagues. When I returned I discovered to my horror that the printer had fallen on the floor due to the carriages acceleration (metal feet of the frame on a hard plastic lab-type stool was a deadly combination). To make things worse the USB cable had almost ripped apart the micro-USB socket. My attempts to fix it did not succeed, so I ended up cutting the micro USB connector off and soldering the four wires directly (incidentally I discovered the schematic had the wiring to D+ and D- reversed on the Reprap site).

Luckily the torn USB connector was the only damage occurred to the printer after the fall. Once fixed it is working I hope as new and giving very good prints using PLA. While very good output quality, I have to admit it is not as good as the i3 MK2, but one of the reasons maybe that the Simple feels faster (in fact it uses default higher accelerations on XY axis than the Prusa i3) which can account for both the faster printing and the small difference in output quality.

All in all, I am impressed at how well this little printer handles. One minor detail is that while the printer profile is small, you need to have good clearance on the sides and on the back so X and Y carriages can move freely.

I can understand why this model has so many good reviews, it is just not intended for tinkering much as all the parts are held in a really compact space.

Thursday, October 06, 2016

Stepper-motor speed profile generation

My 4xiDraw project has been a source of inspiration for other projects. A while ago I mentioned how to add wireless connectivity to a serial-based device, but for a subject I teach I wanted to get a bit deeper on the details about stepper motor timing generation for trapezoidal (or any other) speed profile.

While this functionality is implemented in every CNC or 3D printer controller software, most of them are based on GRBL development, which is efficient but not easy to grasp on a first look. There are many different but related algorithms working together there.

Just by chance I bought a Wemos D1 board that replaces the Arduino UNO Atmega 328 by an ESP8266 but keeps the UNO form factor. It was a weird proposal but I bought anyway as we all know that anything that stamps wifi on it makes it a better product.

I have used the ESP8266 in the past, through the Arduino IDE, but I have never needed to achieve any realtime operation. But once I checked that CNCshield board could be used (and it will work ok) together with Wemos D1, I set my mind to replace the Arduino UNO of my 4xiDraw by the wifi-enabled Wemos sibling.

The good news was that the ESP8266 32 bit processor and generous flash memory space will allow me to get decent performance without much effort on my part while coding. However, I was not so sure about getting good real-time performance on the stepper's step signal.

Timing is everything

Steppers are picky motors. They do not rotate unless the controller keeps sending step signals at the proper pace. Using a fixed rate is the simple alternative, but physics get in the way and this approach is somehow limited. So instead of using a fixed speed, a more common approach is to use a variable speed, starting from a low speed and ramping up to a cruise speed to later decelerate back to a stop. 

Given that the speed of a stepper is directly proportional to the rate of the step signal, we need to create a signal whose rate will increase [linearly] and decrease. But for coding purposes, we need to establish the time period in-between the different steps. 

Unfortunately, given the reverse nature of frequency and period, a linear increase in frequency (speed) does not translate into a linear decrease of the period. Failing to make this point, programmers are in for a big disappointment. 


There is a very interesting application note by ATMEL that goes into a lot of detail on how you can calculate such timer intervals without much computing cost. This is what GRBL and Marlin and Smoothieware do. 

But for a 3D printing or CNC machine we are interested on accurately controlling the position of each move too. That means that for each basic movement, a straight line is drawn from an initial point to a destination point on a multidimensional space. A certain distance to be covered over an axis comes immediately to a given number of steps. It is that total distance what will be traversed using a so called trapezoidal speed pattern that will smooth acceleration and deceleration phases so hopefully motion will happen without missing any steps. This helps the motors to reach much higher speeds than what would be possible when using only a fixed speed, which will contribute to lower 3D printing or machining times too.  

For a stepper motor, the zero speed corresponds to an infinity period in-between steps. We would consider a non-zero initial speed, than will give us a value T0. This value will be decreasing at each step while accelerating and it will remain the same while motor cruises to start increasing again to slow down the stepper till it stops when no more pulses are provided to the step signal of the motor driver.

The time to cover the distance of a step can be formulated as T0 = sqrt ( 2 / accel ) and the period difference between one step n and step n+1 can be expressed as Tn+1 = Tn * ( sqrt( n + 1 ) - sqrt( n ) ). So we could use that for iteratively calculate the new time interval till the next step. Unfortunately, a couple of square roots take time to calculate, even more if using an 8-bit processor. 

Luckily, a series expansion of the expression above allows us to obtain a simpler relationship that can be easily calculated so now Tn = Tn-1 - 2 * Tn-1 / ( 4 * n  + 1 )

Each step there is a slight speed increase, so when the desired maximum speed is reached, no more increases happen. So that last Tn value is kept for the period of all the steps while cruising until the deceleration phase starts, that can use the same sequence of numbers (1..n) but now these will be negative numbers going from -n to -1. 

What is left is to determine the amount of pulses for the acceleration and deceleration phases. For simplicity I considered the same acceleration for both of them, so they will need the same amount of steps. The remaining steps, if any, will be traveled at the maximum speed. Please note that for short movements it may not be possible to reach the desired maximum speed (feedrate) so half of the time will be used accelerating and half of the time decelerating to/from a speed lower than the maximum one. 

Timer0 on ESP

One thing I have not used before was a timer on the ESP. I assumed the Servo library would use one but I did not dig into the details. However, now I wanted to make sure the timing I was carefully calculating for each step would not be disturbed by other tasks the processor might get into when communicating wirelessly. 

My plan was to use a timer so each new step will be scheduled with the help of the timer, that will cause an interrupt at the right time of the next step. Being interrupt-driven should help getting the timing right.

Once configured a new call to timer0_write(ESP.getCycleCount() + 80 * microseconds) will schedule a new interrupt after that number of microseconds from now.  The code of the interrupt will calculated and schedule the time on the new interrupt, plus it will perform the motion on any of the steppers, determining the end of acceleration, the begin of the deceleration or the end of the move.

However, I have found I cannot stop the timer0 from running without getting a watchdog interrupt afterwards, so I depend on whether or not there are more steps to be processed to run the motion related code or a dummy new interrupt is scheduled every 10 milliseconds, just to keep the ball rolling and avoid the watchdog from complaining. Maybe there is a better way but I settled with what worked first. 

The servo

I only needed to get a servo working, so the obvious choice was to use the servo library, but for reasons unknown, it won't work. Not even when defining SERVO_EXCLUDE_TIMER0 to prevent it to use timer0. But that is not really a big deal as I can use some time in the main loop to create a pulse of the desired width (1.5 .. 2 msec) and to refreshed it every 20 msec or so. And so I did and it seems to be working nicely as shown in the sample video below:

The code that was driving the motion was being created in my desktop computer by this line of code:  (while true; do X=$((RANDOM % 100)); Y=$((RANDOM %100)); Z=$((RANDOM % 1000 + 1000)); echo "M3S$Z"; echo "G1 X$X Y$Y"; sleep 2; done ) |nc -u 192.168.4.1 9999

You can get the project code and some extra details, like a logic-analyzer trace and source code from here. Almost forgot: I have based my project on Dan's code from MarginalClever.com