An Electronic Leadscrew Controller using a Pi Pico

As I'm finding out, the UI is where the bulk of the work takes place. The more options, the bigger and more complex the state machine. I'm already at the point of rewriting some code to make it more extensible, because the UI is getting a little awkward. This sort of thing happens when one hasn't planned it all out from the beginning. Of course, when I started, it wasn't clear what to do. I did attempt to storyboard the menus, but didn't foresee some of the issues. No matter, it's been an interesting journey so far! I'll make it work somehow.

One thing that tripped me up was keeping track of all the soft buttons and their scope. That's part of what needs some work. Have an idea how to deal with them, but need to prototype it first. That's the task of the week.

I have to stop fooling around and put my code under configuration control, things are working and I want to be able to return to that state. Probably just run a local instance of git, since this is a small project. Don't yet want to use GitLab or GitHub because I don't want the code to escape to the wild without all the boiler plate disclaimers. If it works out ok, then maybe.
 
Pretty typical. The UI will be 90%+ of the bulk of the code for this. Sometimes it is just better to use the first draft to learn from, toss it and make a design now knowing what you want it to do. Then evolve from there. I have a bunch of lists of what the interface needs to display. I'll probably use a non-touch LCD or OLED and a rotary encoder with push switch for most of it. Select mode, gearbox setting, and overall ratio. Perhaps some dedicated switches for things like ELS normal-off-reverse. Simple interface. Display lots of info with a fairly static display organization. The operator could engage leadscrew, feed, or cross feed drives so should display all of those in both imperial and metric. The mode primarily selects the list of selectable values. Modes are things like imperial threading, metric threading, imperial feeding, metric feeding, specials. I'd like to store the config for those in a file that can easily be edited so no recompile needed to add a new pitch or feed. Perhaps stick it on an SD card. The Pico doesn't have an SD card but the display modules often do.
 
Just as a warning, many of the SD cards on common cheap displays don't work well. I bought such a display, but the vendor was upfront about it. For me it didn't matter, since my processor board has a working SD socket. It also has external pads to add additional RAM or flash memory.

Hadn't really thought much about adding a bunch of switches. My experience with them is they bounce too much, or they fail. That's why I chose to avoid Clough's display and switches, it just felt clumsy and awkward. Soft switches seem to be a lot easier, because it is easy to make them context sensitive. Will attempt to make my control via the soft panel. However, real switches can have nice mechanical feedback. It feels like one is in control when moving them about.
 
I've had a number of LCD displays with SD cardslots but never tried to use the SD slot subsystem. I've seen them used in demos, but that doesn't prove much. A lot of my displays are Adafruit units, perhaps that matters. I wonder if they work better with the older, slower, smaller SD cards. The Pico doesn't have a slot. Something to try later, not a priority now. More than one solution there.

Feed on/off and direction feels like it deserves it's own switch. Probably a three position toggle. Bouncing won't be an issue for that functionality. I'd like to be able to shut off the feed/threading drive when it isn't being used, for example when I'm winding reels with line, or using the tailstock. It needs to be very easy to do or it won't happen.

I'm looking for a simple but quality interface for the UI, and blending it with the lathe's UI. The PM-1228 native UI has pushbuttons and knobs. The reverse feed selector is actually a switch under the hood that moves the gearing (another reason to add ELS). A three position switch for feed seems like a good fit. I don't plan to add switches beyond the rotary encoder / push switch and the feed switch unless it really makes sense in terms of UI functionality.

Another approach would be three rotary encoder knobs instead of one. One for mode, one for gearbox setting, and one for overall ratio. Versus one for all three functions, and pushing it in rotates between functions. One knob is less hardware and fewer wires, and seems quite adequate. Many 3D printers use the one knob with push switch interface with an LCD, it has a much richer interface than we need for the ELS.

Anything else that deserves it's own switch?
 
It takes very little code to read a rotary encoder and generate fractional steps. We don't really need encoder reading hardware in the processor or interrupts when we have a separate core to dedicate to the realtime encoder process. Using a table lookup makes the encoder reading extremely quick and it allows easy detection of read errors when both bits change between successive read cycles.

This same approach works with interrupt on pin change firing the code if there is no separate core available. Interrupts are considerably slower and less predictable than a dedicated core. On most processors it takes longer to save and restore processor state than executing a short handler's actual useful code. If any piece of code anywhere turns the interrupts off for very long encoder steps may be missed.

The Pico PIO processor can be used to implement the lowest level I/O. Generating the proper timing for the stepper motor direction and step outputs would be very useful here. That would guarantee the proper setup and hold times on the signals, generate correct pulse widths and keep the core focused on reading the encoder. So instead of calling a step function send the direction value to the step generating PIO que and let the PIO do the timing and set the direction and toggle the step pins in the state machine.

ELS Pseudocode - Lookup Table based Encoder Process and Generate Fractional Steps by Alan B // lookup table index is old BA<<2 plus new BA, 16 values // A 01100 // B 00110 setup1(): // run in second core, dedicate this core to the real time { table[0000b] = table[0101b] = table[1010b] b= table[1111b] = 0; // no change, no motion table[0001b] = table[0111b] = table[1110b] b= table[1000b] = 1; // cw rotation table[0010b] = table[1011b] = table[1101b] b= table[0100b] = 2; // ccw rotation table[0011b] = table[0110b] = table[1001b] b= table[1100b] = 4; // error, both bits changed } loop1(): // realtime loop reads encoder, converts motion with fraction to steps { spindle_encoder = read_pins() & 3; // read spindle encoder A and B connected to first two IO pins switch(table[spindle_prev+spindle_encoder]) { case 0: // no motion break; case 1: // cw rotation sum += numerator; // advance fraction if (sum >= denominator): // overflow generates output step { sum -= denominator; step(direction_a); // send direction to PIO step generator } break; case 2: // ccw rotation sum -= numerator; // retard fraction if (sum <= -denominator): // underflow with hysteresis when reversing direction { sum += denominator; step(direction_b); // send direction to PIO step generator } break; default: ++errors; // more than one bit changed between read cycles } spindle_prev = spindle_encoder << 2; // store encoder bits for next time }
 
Last edited:
Good. A basic Bresenham loop. It's a good start. Would be even better if it compiles, so I could follow along with my Pico. Have one but I haven't installed any software yet. Seeing real compilable code could motivate me to get off my duff ;)

Which processor would control or manage system states which allows or enables progression to the next allowable state? The UI processor? I would think at a minimum that the stepper or servo would be locked until certain conditions were met, umm like initialization complete (thread or feed set) and maybe a start command. Definitely don't want carriage movement without going through the correct sequence. Somewhere also there should be a process that does bounds checking on key variables as well. I haven't put them in yet, but think it is very important to do since one is controlling machinery. Been thinking about that lately.
 
Just looking through the Pico Arduino documentation at Arduino Pico Docs

Useful to keep in mind when selecting User Interface hardware.

1653493919678.png
FreeRTOS is also available in the Pico Arduino, and access to the Pico SDK and PIO.

There is also a Filesystem that can be used to store the configuration files. Perhaps the USB should be setup to allow access to the filesystem so updating the config files would just involve plugging into a computer and writing or editing the file(s).

At this point I'm leaning toward using the Arduino Pico environment, it's the simplest and most accessible way for most to work with the Pico boards.
 
Just looking through the Pico Arduino documentation at Arduino Pico Docs

Useful to keep in mind when selecting User Interface hardware.

View attachment 407883
FreeRTOS is also available in the Pico Arduino, and access to the Pico SDK and PIO.

There is also a Filesystem that can be used to store the configuration files. Perhaps the USB should be setup to allow access to the filesystem so updating the config files would just involve plugging into a computer and writing or editing the file(s).

At this point I'm leaning toward using the Arduino Pico environment, it's the simplest and most accessible way for most to work with the Pico boards.
Ah, good. Didn't know that they had done the integration with Arduino yet. When the Pico was first announced, that wasn't ready, so a Pico wasn't that interesting to me. Now it's a bit more motivating.
 
Good. A basic Bresenham loop. It's a good start. Would be even better if it compiles, so I could follow along with my Pico. Have one but I haven't installed any software yet. Seeing real compilable code could motivate me to get off my duff ;)

Which processor would control or manage system states which allows or enables progression to the next allowable state? The UI processor? I would think at a minimum that the stepper or servo would be locked until certain conditions were met, umm like initialization complete (thread or feed set) and maybe a start command. Definitely don't want carriage movement without going through the correct sequence. Somewhere also there should be a process that does bounds checking on key variables as well. I haven't put them in yet, but think it is very important to do since one is controlling machinery. Been thinking about that lately.

Yes, many details remain. Most go into the UI core0. I have more detail in the pseudocode that I made some time ago with things like RPM measurement, I just show the basic part here. The UI core could enable/disable the realtime core to stop motor output. Clough42 said he just controlled the motor enable/disable line and left the steps going all the time. That's a reasonable approach and keeps the control quite separate from the realtime core (and you can still watch it on the scope). Get everything sorted out before enabling the motor. Perhaps an enable flag should be checked at the top of the realtime loop. This would put it in a known state while the fraction was updated rather than disabling the core. Or disable the core and then reboot the realtime core so it starts out cleanly. Several ways to go there. If the numerator and denominator are initialized to reasonable values (like 0,1) the loop can start right away, it won't run amok. If we want the Spindle RPM to work the loop needs to run, so perhaps it should always run, just skip the fractional calculations when the enable is false. The UI will depend on the spindle to be stopped, so the RPM needs to be monitored full-time.

I need to decide how to update the fraction while running. While the spindle is moving the UI should be locked except for the case of adjusting feed rates. Only during feeding would live changes to the fraction be allowed, and they have to be done smoothly. The complexity and calculations required will be in the UI, we don't want much impact on the realtime core. One approach would be to use the same denominator for the feed rates, and only vary the numerator. The numerator can be written while running without much issue. We don't really care if the fractions are reduced or not. Since threading won't be changed on the fly there is no requirement for matching the denominator in those tables.

I added some pseudocode to update the fraction synchronously while running to my version here, not the simple version above.

I must finish up a couple of things before I start breadboard work on this. I'm waiting for parts to reassemble the watch, then perhaps I can make some progress there. I have the Pico connected to a Pi4 at the moment and it is set up for MicroPython, so that will have to change. I have a few more Picos so this one may just get parked. I was doing some GPS data processing with it and trying various displays for some clock projects, both with the Pi4 and the Pico. The small OLED display works well with the Pico, I might use it for testing initially. I haven't chosen a larger display for the project yet. Might be nice to 3D print a mount to hold the encoders and a small motor and the breadboard for testing so things aren't flopping around. I should have a small motor and driver board here somewhere. There's a level shifter there that can be used as well.

1653500142698.png
 
Last edited:
That's more like it! Looks like what I had on my desk when I started. Wires and whatnot everywhere. I consolidated my driver receiver boards to a single board just to get some space back.

Just a thought, either a disable on the stepper or just set the numerator to zero? Been out in the sun a bit, perhaps that won't work, but just throwing out the thought.

In my case my spindle position is always updated, but I can control the accumulation. No accumulation, then the stepper never moves. Or just a simple if statement in the stepper routine. I like the latter best because it is obvious what is blocking stepping. Other ways are less obvious. In the midst of some bizarre bug, you want things to be very apparent. Subtlety is often lost when thrashing through the problem. But that's just my preference.
 
Back
Top