Fast Stacker Upgrade

Here I am describing my progress with upgrading my Fast Stacker (focus stacking rail) to hardware version 2.0.
== Interface improvements ==
Things I might implement in the new design:
* Three separate main screens/modes (instead of one when you always see all the parameters, including the ones not relevant to your setup), switchable from the keypad, current state memorized in EEPROM. The name of the screen would show at the top, only the relevant paremetrs would appear, with nice descriptions and associated keys shown. I might assign the same keys to change different parameters, depending on the mode. 
** 1-point continuous
** 2-point continuous
** 2-point stop-n-go. Now I will have screen space to show all the relevant parameters on a single screen (before you'd have to press # first to access some parameters)
* The dangerous keys "4" and "B" (setting fore and background points) can optionally require a long press to do the action - less chance re-setting theses points accidentally. I will add an audible confirmation (a short beep from a buzzer), to make this usable even if you are not looking at the controller.
* I will have a help menu screen (or multiple screens), so I don't have to always carry printed manual with me.
*I'll add a "Parking the motor" command. Apparently the stepper motor driver needs to bring the motor to a full step position integer dividable by 4, before being turned off, to ensure it doesn't move when turned back on. (So it can be 0, 4, 8, ... full steps away from the point when it was turned on.)
*Beeper can be used for more stuff - e.g. a special beep when hitting a limiting switch, when finishing the stacking sequence, optionally for each shot, for each key press etc.
== Parts ==
Preliminary list of the parts to be used (comes up to ~ CAD $40, including the shipping fee):
* WeMos D1 Mini (Rev.1) microcontroller (based on ESP8266)
** CAD $4.40 on ebay
* 1.8" 160x128 pixels TFT LCD (based on ST7735 chip; SPI interface)
** This is the 11-pin version I happened to have (8-pin version is now more common); CAD $13.30 on ebay
* 16-ports SPI I/O Expander chip MCP23S17
** CAD $2 + shipping on digikey.ca
* DRV8825 Stepper Motor Driver
** CAD $3 on ebay
* LD1117V33 Voltage Regulator 3.3V 950mA TO-220
** CAD $1.40 per piece on amazon prime ($8 for 5 pcs)
**CAD $0.84 + shipping on digikey.ca
* 4x4 matrix keypad - similar to this one; the same as in my ver. 1.x controller.
** CAD $4.50 on ebay
**CAD $8.50 + shipping on digikey.ca
* 2 pcs of Sharp PC817 optocouplers (to be used inside the rail unit - replacing the old relays)
** CAD $1.10 + shipping on digikey.ca
* A few 0.125W resistors, electrolyte capacitors, and LEDs
* Mini-piezo PKM13EPYH4000-A0
** CAD $0.50 + shipping on digikey.ca
Five copies of the PCB will cost around CAD $20 with shipping (jlcpcb.com).
== Display ==
I managed to hook up this 1.8" 160x128 pixels TFT LCD (based on ST7735 chip) to my WeMos D1 Mini (Rev.1) microcontroller, using hardware SPI:
Datasheet
Amazon link; $15 CAD
The following display library is the fastest for my setup, from my tests:
* TFT eSP
My display runs fine up to SPI clock rate 40 MHz ("#define SPI_FREQUENCY 40000000" in User_Setup.h). The full list of my customizations in User_Setup.h:
 #define TFT_WIDTH 128
 #define TFT_HEIGHT 160
 #define ST7735_BLACKTAB
 #define TFT_CS PIN_D0 // Chip select control pin 
 #define TFT_DC PIN_D4 // Data Command control pin
 #define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
 #define SPI_FREQUENCY 40000000
At 40 MHz SPI clock I got the following timings (compared to the official Adafruit ST7735 library which I believe is set to use 32 MHz clock):
* Print a 6-char cursor with background erasing
** eSPI: 0.906 ms (10x8 pixel chars)
** Adafruit: 4.46 ms (8x8 pixel chars)
* Draw a custom 8x8 bit bitmap
** eSPI: 0.412 ms
** Adafruit: 1.1 ms
My wiring is as follows:
Function 	D1 Mini	LCD
+3.3V VCC	3.3V 	VCC
Ground 	G 	GND
Reset 	RST 	RST
DC 	D4 	RS
Clock 	D5 	CLK
MOSI 	D7 	SDA
CS 	D0 	CS
I also need:
* 8 DPs for the keypad (speed not important, so will use the expander pins) - say, all GPAx expander pins.
* Two fast pins for the stepper motor (step, direction): D1, D2
* Other stepper motor pins (not fast) - Enable, perhaps step size (1-4 pins): from the expander
* Two expander pins for the camera (shutter, AF), via optocouplers
Apparently there is a way to use TX and RX safely, as general purpose GPIOs.
== GPIO expander ==
I plan to use the chip (not module) MCP23S17 as the expander.
Datasheet.
* 16 GPIOs
* SPI - limited to 10 MHz (so requires editing of the library).
* Operatinng voltage 1.8-5V
* Maximum current
** Per pin: 25 mA
** Total: 150 mA
Library
* The library has SPI clock divider set to 2 - "#define CLOCK_DIVIDER (2)" in MCP23S17.cpp - which would mean 80 MHz clock for my 160 MHz D1 Mini, which cannot be true. (MCP chip can only operate at up to 10 MHz). Another interesting point is that my display runs fine with the clock of 40 MHz, connected to the same SPI (only the CS pin is different), which is great.
* From my measurements, the expander's pins are significantly slower than native pins, so cannot be used for any functions requiring sub-microsecond response time (STEP and DIR pin for the stepper motor driver, limiter switch sensor).
** Digital Read from a native pin: 0.34 us
** Digital Read from an expander pin: 7.9 us (23x slower!)
My wiring is as follows:
Function 	D1 Mini	MCP23S17
+3.3V VCC	+3.3V 	VCC
Ground 	G 	GND
CS 	D3 	CS
Clock 	D5* 	SCK
MOSI 	D7* 	MOSI
MISO 	D6 	MISO
(*): pins shared with another SPI device (display). D6 (MISO) connection is required for the chip to work (to read values from the expander's pins), even though I do not explicitly define this pin anywhere. It's probably a default MISO pin in ESP8266 platform settings.
Display + port expander + keypad are working now:
With expander + keypad, Arduino loop takes a very short time - 2.64 us (CPU @160 MHz). We'll see how much overhead display adds - perhaps I can even afford continuous (during stacking / rewinding) display refresh, and still get very high stepper motor speeds.
== Keypad ==
Here is the pinout for my keypad:
I connected the keypad pins 1, 2, 3, ..., 8 to the expander's ports A0, A1, ..., A7 (programmatic pin numbers 1, 2, ..., 8). In the Keypad library, I have to do the following setup:
 const byte rows = 4; //four rows
 const byte cols = 4; //three columns
 char keys[rows][cols] = {
 {'1', '2', '3', 'A'},
 {'4', '5', '6', 'B'},
 {'7', '8', '9', 'C'},
 {'*', '0', '#', 'D'}
 };
 byte rowPins[rows] = {5, 6, 7, 8}; //connect to the row pinouts of the keypad
 byte colPins[cols] = {1, 2, 3, 4}; //connect to the column pinouts of the keypad
 Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols );
== Stepper motor driver ==
I am using DRV8825 Stepper Motor Driver.
My wiring is as follows:
Function 	D1 Mini	MCP23S17	DRV8825
Reset 	+3.3V 	- 	Reset
Sleep 	+3.3V 	- 	Sleep
Ground 	G 	- 	GND
Step 	D1 	- 	Step
Direction	D2 	- 	Dir
Enable 	- 	B0 (9) 	Enable
M0 	- 	B1 (10) 	M0
M1 	- 	B2 (11) 	M1
M2 	- 	B3 (12) 	M2
== Schematics ==
Here is the preliminary schematics for the new controller.
=== Notes ===
* R1 value. The smaller the better (minimizes interference inside the Cat5 cable from the stepper motor high-current wires -> minimizes a chance of a false limiting switch triggering), but cannot be smaller than 275 Ohm (because otherwise the DP of the microcontroller will get overloaded - it cannot handle more than 12 mA). Also, if it is too small, too much current will be constantly wasted (a problem when running from batteries). 3.3k (resulting in 1 mA constant current loss) is a good starting point. The current is still 4x larger than in my old setup where I used internal Arduino pullup resistors (5V/20k=0.25 mA current), so I should see a significant improvement in terms of minimizing fake limiter switch triggers. If it still remains a problem, I can further lower R1 down (say, to 1k).
* R2 / R3 values. This is a voltage divider. The values of 68k and 10k allows one to safely measure the input voltage up to 3.2V*(10k+68k)/10k=25V, which is a good safety margin for the actual voltage (12V). The constant current flow is 12V/(10k+68k)=150uA which is perfectly acceptable.
* R4 / R5 values. These are current limiting resistors for the two PC817 optocouplers (will be located inside the rail unit). Forward voltage for these is 1.2V. With the logic voltage 3.3V, with 1k limiting resistor we get current (3.3V-1.2V)/1k ~ 2mA. With CTR>=0.5, this can drive the load up to 1mA - likely sufficient to trigger shutter and AF of the DSLR. Also, 2mA is large enough to minimize the noise induced by the stepper motor wires in the same Cat5 cable. If the noise is till too large and/or the output current is not large enough to trigger the shutter/AF, you can decrease R4/R5, with the smallest allowed value being 42 Ohm (results in the maximum allowed input current - 50 mA). For the reference, my 3D Scanner gadget uses 5V/200 Ohm = 25mA input current with PC817, and that is sufficient to trigger my Canon 6D shutter and AF.
* R6 value: probably around 10k. Transistor BC545 has hFE=110-800. The one I have has hFE=329. Maximum collector current Ic is 100 mA. (That means that the load resistance cannot be smaller than 12V/100mA=120 Ohm.) With R6=10k, the base current is 0.33 mA, this is more than enough to get the full 100 mA load current (0.33 mA * 329 = 109 mA). From this calculator, even with the smallest load resistance (120 Ohm), the base resistor <= 10.9k can completely saturate the transistor. So R6=10k should work for any allowed load.
* R7 value. R7 is the discharge resistor for my passive mini-piezo (PKM13EPYH4000-A0). The datasheets suggest the value of 1k, so it'll be my starting point. I can play with it trying to increase the piezo volume (should make sure I don't overload the transistor - it can handle loads up to 100 mA). With the piezo's capacitance C=5.5nF and R7=1k, the RC timing is 190 kHz - more than enough to work well at the rated frequency of the piezo (4 kHz).
* R8 / R9 values. Choose values when the red and green LEDs have similar brightness, and comparable in brightness to the TFT display. My red LED is much brighter than the green LED, so my values are quite different for the two resistors (8k and 1k).
* C4 value, 47 uF. With R7=10k, forms an RC filter with the frequency of 0.3 Hz - should be sufficient to smoothen measuremnt noise for the VCC voltage.
== PCB ==
I am using EasyEDA to design a PCB for the controller. Current design:
== Putting it together ==
I got the PCB, soldered all the parts, and everything is working now! Time to start designing the 3D enclosure (while continuing working on improving the software).
Update (31.05.2021): My v2.0 controller is done, and it works! A short test video: https://www.youtube.com/watch?v=BTTtfg4MGyU
Update (25.08.2021): Software updating and debugging for the s2.0 version is finished. I will be posting detailed tutorials on the new Fast Stacket shortly.