I2C I/O Expander PCB with 32 channels, WeltonCopy board
This PCB peripheral uses two MCP23017 chips to give 32 programmable input/output general purpose connections. These are digital inputs/outputs; pull-up and interrupt options etc. are available. The pin layout involves doubled up digital outputs to go with other existing peripherals such as the relay board and the stepper motor drivers. There will be an equivalent board without the doubling up coming soon i.e. fewer output pins, but the same number of channels. The boards are designed to be daisy-chained allowing huge numbers of digital outputs and soon PWM outputs provided by the Scampton PCB. A maximum of four WeltonCopy boards can be run off of one RPi Pico, providing 128 digital I/O channels per bus; the Pico can have two buses connected. 25mA is available from any output pin, so small devices can be driven, such as off/on LEDs. Between the digital I/O and the PWM option a model train layout could have many off/on lights and signals and also dimmable LED lights. These options can also provide a large number of train track speed and direction controls. Also note that though the Bluetooth Remote Control can be used to control the Digital and PWM outputs, it will be possible to create programs to run on laptops and tablets to allow large numbers of outputs to be controlled by a GUI. An upgraded version of Lincoln Code is planned for this development, which will probably be Visual Studio DotNet to start with. The WeltonCopy board can either regulate 5V down to 3.3V or it can run directly from the external (5V) supply. It does not offer to run the board from MCU I2C cable power at this time, this is because the intent is to supply greater load than most MCUs are rated for.
The unpopulated WeltonCopy peripheral PCB is available for $10NZD plus postage. The lower WeltonCopy peripheral PCB populated as shown is available for $40NZD plus postage. The 5V power supply and the upper parent board are not included. The I2C cable is included.

from machine import Pin, I2C
import mcp23017
import time
#i2c = I2C(scl=Pin(22), sda=Pin(21))
i2c = I2C(id=0, scl=Pin(1), sda=Pin(0))
mcp = mcp23017.MCP23017(i2c, 0x20) # 0x20, 0x21
mcp1 = mcp23017.MCP23017(i2c, 0x21) # 0x20, 0x21
# property interface 16-bit
mcp.mode = 0x0000 # all set to outputs
mcp.gpio = 0x5555 # write to outputs
# property interface 16-bit
mcp1.mode = 0x0000
mcp1.gpio = 0x5555
databyte = 0x5555
while True:
databyte ^= 0xffff
mcp.gpio = databyte
mcp1.gpio = databyte
print('{0:016b}'.format(databyte))
time.sleep(2)
--------------------------------------------------------------------------------
#Program output & voltage measurements confirm behavior
MPY: soft reboot
1010101010101010
0101010101010101
1010101010101010
0101010101010101
1010101010101010
0101010101010101
1010101010101010
0101010101010101
--------------------------------------------------------------------------------
# iopin.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
from machine import Pin, I2C
import mcp23017
import time
class IoPin:
ioaddr=[0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27]
pinlist=[[],[],[],[],[],[],[],[]]
pinlist1=[[],[],[],[],[],[],[],[]]
@classmethod
def updatepinlist(cls, iopin, i2c_channel, i2c_periph_addr_0, i2c_periph_addr_1):
if i2c_periph_addr_0 not in cls.ioaddr or i2c_periph_addr_1 not in cls.ioaddr:
raise ValueError("Invalid peripheral address provided")
pl=cls.pinlist if i2c_channel==0 else cls.pinlist1
idx0 = cls.ioaddr.index(i2c_periph_addr_0)
idx1 = cls.ioaddr.index(i2c_periph_addr_1)
if pl[idx0] != pl[idx1]:
raise ValueError("Pin list mismatch, pin " + str(iopin))
if iopin not in pl[idx0] and iopin not in pl[idx1]:
pl[idx0].append(iopin)
pl[idx1].append(iopin)
else:
raise ValueError("Duplicate pin instance, pin " + str(iopin))
def __init__(self, iopin=None, i2c_channel=0, scl_pin=1, sda_pin=0, i2c_periph_addr_0 = 0x20,
i2c_periph_addr_1 = 0x21):
self._i2c = I2C(id=i2c_channel, scl=Pin(scl_pin), sda=Pin(sda_pin))
self._mcp = mcp23017.MCP23017(self._i2c, i2c_periph_addr_0)
self._mcp1 = mcp23017.MCP23017(self._i2c, i2c_periph_addr_1)
self._value=0
if iopin is None :
raise TypeError("No index provided")
elif iopin < 0 or iopin >= 32:
raise ValueError("Invalid pin ID provided")
else:
self._iopin=iopin
#self._mcp.mode = 0
#self._mcp1.mode = 0
self.updatepinlist(iopin, i2c_channel, i2c_periph_addr_0, i2c_periph_addr_1)
@property
def iopin(self):
return self._iopin
@property
def value(self):
return self._value
@value.setter
def value(self, value):
if value > 1:
self._value=1
elif value < 0:
self._value=0
else:
self._value=value
if self._iopin < 16 and self._iopin >= 0:
self._mcp[self._iopin].value(self._value)
elif self._iopin < 32 and self._iopin >= 16:
self._mcp1[self._iopin - 16].value(self._value)
def output(self, value=None):
if value is None:
self._value=None
elif value > 1:
self._value=1
elif value < 0:
self._value=0
else:
self._value=value
if self._iopin < 16 and self._iopin >= 0:
self._mcp[self._iopin].output(self._value)
elif self._iopin < 32 and self._iopin >= 16:
self._mcp1[self._iopin - 16].output(self._value)
def inputmode(self, pull=None):
if self._iopin < 16 and self._iopin >= 0:
self._mcp[self._iopin].input(pull)
elif self._iopin < 32 and self._iopin >= 16:
self._mcp1[self._iopin - 16].input(pull)
@property
def mode(self):
return self._mcp.mode | (self._mcp1.mode << 16)
@mode.setter
def mode(self, val):
self._mcp.mode = val
self._mcp1.mode = (val >> 16)
@property
def gpio(self):
return self._mcp.gpio | (self._mcp1.gpio << 16)
@gpio.setter
def gpio(self, val):
self._mcp.gpio = val
self._mcp1.gpio = (val >> 16)
@property
def output_latch(self):
return self._mcp.output_latch | (self._mcp1.output_latch << 16)
@output_latch.setter
def output_latch(self, value):
if value is None:
self._value=None
elif value > 1:
self._value=1
elif value < 0:
self._value=0
else:
self._value=value
if self._iopin < 16 and self._iopin >= 0:
self._mcp.output_latch=self._value
elif self._iopin < 32 and self._iopin >= 16:
self._mcp1.output_latch=self._value
--------------------------------------------------------------------------------
# IoPinMain.py
import iopin
import time
pin1 = iopin.IoPin(iopin=1)
pin2 = iopin.IoPin(iopin=2)
#pin1a = iopin.IoPin(iopin=1)
print(iopin.IoPin.pinlist)
print(iopin.IoPin.pinlist1)
while True:
pin1.output(1)
print(pin1.value)
time.sleep(2)
pin1.output(0)
print(pin1.value)
time.sleep(2)
--------------------------------------------------------------------------------
pin1a = iopin.IoPin(iopin=1) # uncommented. Duplicate pin.
MPY: soft reboot
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
File "iopin.py", line 53, in __init__
File "iopin.py", line 30, in updatepinlist
ValueError: Duplicate pin instance, pin 1
>>>

# This code runs, but so far have not connected a motor
# latchiopin.py
# MIT License (MIT)
# Copyright (c) 2024 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 2/7/2025
# import latchiopin
# alatch0=latchiopin.LatchIoPin(0)
# alatch1=latchiopin.LatchIoPin(1)
# alatch2=latchiopin.LatchIoPin(2)
# alatch0.d0.value=1
#from machine import Pin
import iopin
class LatchIoPin:
def __init__(self, iopinbyte=None, bus=0, scl=1, sda=0, ioaddr0=0x20, ioaddr1=0x21, pwmaddr=0x40, reset=False):
if iopinbyte is None :
raise TypeError("No byte index provided")
elif iopinbyte < 0 or iopinbyte >= 4:
raise ValueError("Invalid byte index provided")
else:
self._iopinbyte=iopinbyte
self._iopn=[]
for i in range(8):
self._iopn.append(iopin.IoPin(iopin=i + (self._iopinbyte * 8), i2c_channel=bus, scl_pin=scl, sda_pin=sda,
i2c_periph_addr_0=ioaddr0, i2c_periph_addr_1=ioaddr1))
self.d0 = Channel(self, 0)
self.d1 = Channel(self, 1)
self.d2 = Channel(self, 2)
self.d3 = Channel(self, 3)
self.d4 = Channel(self, 4)
self.d5 = Channel(self, 5)
self.d6 = Channel(self, 6)
self.d7 = Channel(self, 7)
if reset:
self.d0.output(0)
self.d1.output(0)
self.d2.output(0)
self.d3.output(0)
self.d4.output(0)
self.d5.output(0)
self.d6.output(0)
self.d7.output(0)
else:
self.d0.output(self._iopn[0].value)
self.d1.output(self._iopn[1].value)
self.d2.output(self._iopn[2].value)
self.d3.output(self._iopn[3].value)
self.d4.output(self._iopn[4].value)
self.d5.output(self._iopn[5].value)
self.d6.output(self._iopn[6].value)
self.d7.output(self._iopn[7].value)
def _set_value(self, channel, val):
self._iopn[channel.channel_index].value=bool(val)
def _output(self, channel, val):
self._iopn[channel.channel_index].output(bool(val))
def reset(self):
self.d0.output(0)
self.d1.output(0)
self.d2.output(0)
self.d3.output(0)
self.d4.output(0)
self.d5.output(0)
self.d6.output(0)
self.d7.output(0)
class Channel:
"""An instance of a single channel for a multi-channel address latch.
**All available channels are created automatically and should not be created by the user**"""
def __init__(self, alat_instance, index):
self._alat = alat_instance
self.channel_index = index
self._value=0
@property
def value(self):
"""The current value for the channel."""
return self._value
@value.setter
def value(self, value):
self._value=value
self._alat._set_value(self, value)
def output(self, value):
self._value=value
self._alat._output(self, value)
-----------------------------------------------------------------
# latchiopin_main.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 24/7/2025
import latchiopin
import time
import sys
#import iopin
latchdrv0=latchiopin.LatchIoPin(0)
#ipin0=iopin.IoPin(0) # if uncommented causes exception due to duplicate pin
print ("Press enter to continue.")
while 1:
latchdrv0.d0.value = 1
time.sleep_ms(500)
print ("latchdrv0.d0.value ", latchdrv0.d0.value)
data = sys.stdin.buffer.read(2)
latchdrv0.d0.value = 0
print ("latchdrv0.d0.value ", latchdrv0.d0.value)
latchdrv0.d1.value = 1
time.sleep_ms(500)
print ("latchdrv0.d1.value ", latchdrv0.d1.value)
data = sys.stdin.buffer.read(2)
latchdrv0.d1.value = 0
print ("latchdrv0.d1.value ", latchdrv0.d1.value)
latchdrv0.d2.value = 1
time.sleep_ms(500)
print ("latchdrv0.d2.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d2.value = 0
latchdrv0.d3.value = 1
time.sleep_ms(500)
print ("latchdrv0.d3.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d3.value = 0
latchdrv0.d4.value = 1
time.sleep_ms(500)
print ("latchdrv0.d4.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d4.value = 0
latchdrv0.d5.value = 1
time.sleep_ms(500)
print ("latchdrv0.d5.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d5.value = 0
latchdrv0.d6.value = 1
time.sleep_ms(500)
print ("latchdrv0.d6.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d6.value = 0
latchdrv0.d7.value = 1
time.sleep_ms(500)
print ("latchdrv0.d7.value")
data = sys.stdin.buffer.read(2)
latchdrv0.d7.value = 0
-----------------------------------------------------------------
# iopinstepper1.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 22/7/2025
# import iopinstepper1
# stpdrv0=iopinstepper1.StepDrv(iopinbyte=1)
# stpdrv0.DV1.ms1 = 1
# stpdrv0.DV1.dir = 1
# stpdrv0.DV1.step()
import latchiopin
import time
class StepDrv:
def __init__(self, latchid, bus=0, scl=1, sda=0, ioaddr0=0x20, ioaddr1=0x21, pwmaddr=0x40):
self.alatchn=latchiopin.LatchIoPin(iopinbyte=latchid, reset=True, bus=bus, scl=scl,
sda=sda, ioaddr0=ioaddr0, ioaddr1=ioaddr1, pwmaddr=pwmaddr)
self.DV1 = Driver(self, 1)
@property
def enable(self):
return self.alatchn.d7.value
@enable.setter
def enable(self, value):
self.alatchn.d7.value=value
@property
def reset(self):
return self.alatchn.d3.value
@reset.setter
def reset(self, value):
self.alatchn.d3.value=value
@property
def sleep(self):
return self.alatchn.D2.value
@sleep.setter
def sleep(self, value):
self.alatchn.d2.value=value
def _step(self, driver):
Dbit = self.alatchn.d1
if Dbit != "":
Dbit.value = 0
time.sleep_us(2)
Dbit.value = 1
time.sleep_us(2)
#time.sleep_ms(100) # debugging
Dbit.value = 0
#def _set_value(self, channel, val):
def _set_ms1(self, driver, val):
self._setalatbit(6, val)
def _set_ms2(self, driver, val):
self._setalatbit(5, val)
def _set_ms3(self, driver, val):
self._setalatbit( 4, val)
def _set_dir(self, driver, val):
self._setalatbit(0, val)
def _setalatbit(self, dbitindex, val):
if dbitindex == 0:
self.alatchn.d0.value = val
elif dbitindex == 1:
self.alatchn.d1.value = val
elif dbitindex == 2:
self.alatchn.d2.value = val
elif dbitindex == 3:
self.alatchn.d3.value = val
elif dbitindex == 4:
self.alatchn.d4.value = val
elif dbitindex == 5:
self.alatchn.d5.value = val
elif dbitindex == 6:
self.alatchn.d6.value = val
elif dbitindex == 7:
self.alatchn.d7.value = val
def resetmode(self):
self.alatchn.reset()
class Driver:
def __init__(self, stpdv_instance, index):
self._stpdv = stpdv_instance
self.driver_index = index
self._ms1=0
self._ms2=0
self._ms3=0
self._dir=0
@property
def ms1(self):
return self._ms1
@property
def ms2(self):
return self._ms2
@property
def ms3(self):
return self._ms3
@property
def direction(self):
return self._direction
@ms1.setter
def ms1(self, value):
self._ms1=value
self._stpdv._set_ms1(self, value)
@ms2.setter
def ms2(self, value):
self._ms2=value
self._stpdv._set_ms2(self, value)
@ms3.setter
def ms3(self, value):
self._ms3=value
self._stpdv._set_ms3(self, value)
@direction.setter
def direction(self, value):
self._dir=value
self._stpdv._set_dir(self, value)
def step(self):
self._stpdv._step(self)
------------------------------------------------------------------
# iopinstep1_main.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 22/7/2025
import iopinstepper1
import time
import sys
stpdrv0=iopinstepper1.StepDrv(1)
print ("Press enter to continue.")
while 1:
stpdrv0.DV1.ms1 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms1")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms1 = 0
stpdrv0.DV1.ms2 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms2")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms2 = 0
stpdrv0.DV1.ms3 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms3")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms3 = 0
stpdrv0.DV1.direction = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.direction")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.direction = 0
stpdrv0.enable = 1
time.sleep_ms(500)
print ("stpdrv0.enable")
data = sys.stdin.buffer.read(2)
stpdrv0.enable = 0
stpdrv0.reset = 1
time.sleep_ms(500)
print ("stpdrv0.reset")
data = sys.stdin.buffer.read(2)
stpdrv0.reset = 0
stpdrv0.sleep = 1
time.sleep_ms(500)
print ("stpdrv0.sleep")
data = sys.stdin.buffer.read(2)
stpdrv0.sleep = 0
stpdrv0.DV1.step()

# This code runs, but so far have not connected a motor
# iopinstepper3.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 22/7/2025
# import iopinstepper3
# stpdrv0=iopinstepper3.StepDrv()
# stpdrv0.DV1.ms1 = 1
# stpdrv0.DV2.dir = 1
# stpdrv0.DV3.step()
import latchiopin
import time
class StepDrv:
def __init__(self, latchid0, latchid1, bus=0, scl=1, sda=0, ioaddr0=0x20,
ioaddr1=0x21, pwmaddr=0x40):
self.alatch1=latchiopin.LatchIoPin(iopinbyte=latchid0, reset=True, bus=bus, scl=scl,
sda=sda, ioaddr0=ioaddr0, ioaddr1=ioaddr1, pwmaddr=pwmaddr)
self.alatch2=latchiopin.LatchIoPin(iopinbyte=latchid1, reset=True, bus=bus, scl=scl,
sda=sda, ioaddr0=ioaddr0, ioaddr1=ioaddr1, pwmaddr=pwmaddr)
self.DV1 = Driver(self, 1)
self.DV2 = Driver(self, 2)
self.DV3 = Driver(self, 3)
@property
def spare(self):
return self.alatch2.d0.value
@spare.setter
def spare(self, value):
self.alatch2.d0.value=value
def _step(self, driver):
dbitindex = {3:2,2:7,1:4}[driver.driver_index]
dbyteindex = {3:2,2:2,1:1}[driver.driver_index]
alatch = None
if dbyteindex == 1:
alatch = self.alatch1
else:
alatch = self.alatch2
Dbit = None
if dbitindex == 2:
Dbit = alatch.d2
elif dbitindex == 4:
Dbit = alatch.d4
elif dbitindex == 7:
Dbit = alatch.d7
if Dbit is not None:
Dbit.value = 0
time.sleep_us(2)
Dbit.value = 1
time.sleep_us(2)
Dbit.value = 0
def _setalatbit(self, dbyteindex, dbitindex, val):
alatch = ""
if dbyteindex == 1:
alatch = self.alatch1
else:
alatch = self.alatch2
if dbitindex == 0:
alatch.d0.value = val
elif dbitindex == 1:
alatch.d1.value = val
elif dbitindex == 2:
alatch.d2.value = val
elif dbitindex == 3:
alatch.d3.value = val
elif dbitindex == 4:
alatch.d4.value = val
elif dbitindex == 5:
alatch.d5.value = val
elif dbitindex == 6:
alatch.d6.value = val
elif dbitindex == 7:
alatch.d7.value = val def resetmode(self):
self.alatchn.reset()
def _set_ms1(self, driver, val):
dbitindex = {3:5,2:2,1:7}[driver.driver_index]
dbyteindex = {3:2,2:1,1:1}[driver.driver_index]
self._setalatbit(dbyteindex, dbitindex, val)
def _set_ms2(self, driver, val):
dbitindex = {3:4,2:1,1:6}[driver.driver_index]
dbyteindex = {3:2,2:1,1:1}[driver.driver_index]
self._setalatbit(dbyteindex, dbitindex, val)
def _set_ms3(self, driver, val):
dbitindex = {3:3,2:0,1:5}[driver.driver_index]
dbyteindex = {3:2,2:1,1:1}[driver.driver_index]
self._setalatbit(dbyteindex, dbitindex, val)
def _set_dir(self, driver, val):
dbitindex = {3:1,2:6,1:3}[driver.driver_index]
dbyteindex = {3:2,2:2,1:1}[driver.driver_index]
self._setalatbit(dbyteindex, dbitindex, val)
def resetmode(self):
self.alatch1.reset()
self.alatch2.reset()
class Driver:
def __init__(self, stpdv_instance, index):
self._stpdv = stpdv_instance
self.driver_index = index
self._ms1=0
self._ms2=0
self._ms3=0
self._dir=0
@property
def ms1(self):
return self._ms1
@property
def ms2(self):
return self._ms2
@property
def ms3(self):
return self._ms3
@property
def direction(self):
return self._direction
@ms1.setter
def ms1(self, value):
self._ms1=value
self._stpdv._set_ms1(self, value)
@ms2.setter
def ms2(self, value):
self._ms2=value
self._stpdv._set_ms2(self, value)
@ms3.setter
def ms3(self, value):
self._ms3=value
self._stpdv._set_ms3(self, value)
@direction.setter
def direction(self, value):
self._dir=value
self._stpdv._set_dir(self, value)
def step(self):
self._stpdv._step(self)
-------------------------------------------------------------------------------
# This code runs, but so far have not connected a motor
# iopinstep3_main.py
# MIT License (MIT)
# Copyright (c) 2025 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 22/7/2025
import iopinstepper3
import time
import sys
stpdrv0=iopinstepper3.StepDrv(0, 1)
print ("Press enter to continue.")
while 1:
stpdrv0.DV1.ms1 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms1")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms1 = 0
stpdrv0.DV1.ms2 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms2")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms2 = 0
stpdrv0.DV1.ms3 = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.ms3")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.ms3 = 0
stpdrv0.DV1.direction = 1
time.sleep_ms(500)
print ("stpdrv0.DV1.direction")
data = sys.stdin.buffer.read(2)
stpdrv0.DV1.direction = 0
stpdrv0.DV2.ms1 = 1
time.sleep_ms(500)
print ("stpdrv0.DV2.ms1")
data = sys.stdin.buffer.read(2)
stpdrv0.DV2.ms1 = 0
stpdrv0.DV2.ms2 = 1
time.sleep_ms(500)
print ("stpdrv0.DV2.ms2")
data = sys.stdin.buffer.read(2)
stpdrv0.DV2.ms2 = 0
stpdrv0.DV2.ms3 = 1
time.sleep_ms(500)
print ("stpdrv0.DV2.ms3")
data = sys.stdin.buffer.read(2)
stpdrv0.DV2.ms3 = 0
stpdrv0.DV2.direction = 1
time.sleep_ms(500)
print ("stpdrv0.DV2.direction")
data = sys.stdin.buffer.read(2)
stpdrv0.DV2.direction = 0
stpdrv0.DV3.ms1 = 1
time.sleep_ms(500)
print ("stpdrv0.DV3.ms1")
data = sys.stdin.buffer.read(2)
stpdrv0.DV3.ms1 = 0
stpdrv0.DV3.ms2 = 1
time.sleep_ms(500)
print ("stpdrv0.DV3.ms2")
data = sys.stdin.buffer.read(2)
stpdrv0.DV3.ms2 = 0
stpdrv0.DV3.ms3 = 1
time.sleep_ms(500)
print ("stpdrv0.DV3.ms3")
data = sys.stdin.buffer.read(2)
stpdrv0.DV3.ms3 = 0
stpdrv0.DV3.direction = 1
time.sleep_ms(500)
print ("stpdrv0.DV3.direction")
data = sys.stdin.buffer.read(2)
stpdrv0.DV3.direction = 0
stpdrv0.DV1.step()
stpdrv0.DV2.step()
stpdrv0.DV3.step()
stpdrv0.spare = 1
time.sleep_ms(500)
print ("stpdrv0.spare")
data = sys.stdin.buffer.read(2)
stpdrv0.spare = 0
Stepper driving using A4988 and IoExpander
The iopinstepper1.py software driver has been tested successfully. It only takes 2us to trigger a step so multiple motors of various kinds can be run simultaneously. See Video. See PWM page. The A4988 does not require a PWM signal and so digital IO only is sufficient to drive it.
