Digital I/O Expander Testing: Inputs
Digital I/O Expander Testing: Inputs#
Today I continued the testing I started yesterday, this time looking at the input side of the Digital I/O Expander building block board.
The first test was a basic input check. I wrote a small script to read the input pins and print the result to the serial console. The plan was simple: set each input high one by one, then check that the board was reading the input correctly.
import machine
import time
led = machine.Pin("LED", machine.Pin.OUT)
indata = machine.Pin(6, machine.Pin.IN)
_inload = machine.Pin(8, machine.Pin.OUT)
inclk = machine.Pin(9, machine.Pin.OUT)
while True:
led.toggle()
_inload.value(0)
time.sleep(0.0001)
_inload.value(1)
bitstream = ""
for i in range(16):
readbit = indata.value()
bitstream = bitstream + str(readbit)
inclk.value(0)
time.sleep(0.0001)
inclk.value(1)
print(bitstream)
This was when I encountered the first issue. Toggling the inputs did nothing. There was no change in the value being read and nothing was registering.
I checked the pins and everything looked okay. I ran the same test again, checked the pins again, and then noticed that one of the wires had come loose ever so slightly from the little black Dupont connector. I wiggled it around for a bit and the value changed to a 1, then back to a 0, then to a 1 again. I then realised I probably should have started testing properly caffeinated, so I nipped to the kitchen to rectify that.
So that was issue one: a dodgy wire.
After replacing the wire, the inputs started registering correctly, although not in the position I expected. That was when the second issue appeared. Thankfully, this one was a silkscreen problem rather than an electronics problem.
The labelling was reversed across each 8-input bank: I0 corresponded to I7, I7 corresponded to I0, I8 corresponded to I15, and I15 corresponded to I8.
That resulted in a quick update to the PCB layout for a possible Rev B run when I get around to it.
In practice, this is not a major problem. I can live with it for now and it does not stop me testing the rest of the board. The electronics are doing what they should; the silkscreen just needs correcting before I send another batch of boards for manufacture.
I then moved on to a more useful test: reading the inputs and writing them directly to the corresponding outputs.
The aim was to make sure that the actual input I0, not the incorrectly labelled silkscreen position, controlled output O0, and so on up to I15 controlling O15.
import machine
import time
led = machine.Pin("LED", machine.Pin.OUT)
outclk = machine.Pin(1, machine.Pin.OUT)
outlatch = machine.Pin(2, machine.Pin.OUT)
_outen = machine.Pin(3, machine.Pin.OUT)
outdata = machine.Pin(4, machine.Pin.OUT)
_outclr = machine.Pin(5, machine.Pin.OUT)
indata = machine.Pin(6, machine.Pin.IN)
_indata = machine.Pin(7, machine.Pin.IN) # Spare/inverted input line, not used in this test.
_inload = machine.Pin(8, machine.Pin.OUT)
inclk = machine.Pin(9, machine.Pin.OUT)
_outen.value(0)
_outclr.value(0)
time.sleep(0.1)
_outclr.value(1)
while True:
led.toggle()
_inload.value(0)
time.sleep(0.0001)
_inload.value(1)
bitstream = 0
bittoset = 0
for i in range(16):
readbit = indata.value()
# Set the relevant bit in the bitstream variable.
if readbit:
bitstream = bitstream | (1 << i)
else:
bitstream = bitstream & ~(1 << i)
inclk.value(0)
time.sleep(0.0001)
inclk.value(1)
# print(bitstream)
outlatch.value(0)
outclk.value(0)
time.sleep(0.1)
for i in range(16):
outclk.value(0)
time.sleep(0.0001)
bittoset = (bitstream >> (15 - i)) & 1
outdata.value(bittoset)
led.value(bittoset)
outclk.value(1)
time.sleep(0.0001)
outlatch.value(1)
This test worked. All inputs were correctly mirrored to the outputs and each output could be controlled individually as expected.
I did notice a performance issue while testing, but that was mostly down to the test code itself. Continually printing to the debug console slows things down, and the script also includes deliberate delays to make testing easier. Once the debug output was removed and the timing delays were reduced, the performance was acceptable for my purposes.
So, apart from a dodgy wire and a bit of a mess with the silkscreen labels, the board is working as expected.
I will take that as a win for today.
As the code above shows, there is not a huge amount required to get something useful out of the board. The process is simply a case of configuring the GPIO pins, loading the input shift registers, clocking the data in, then clocking the output data back out.
It is straightforward stuff, which is exactly what I want from these building block boards.
The next step will probably be to tidy the code up and start encapsulating the input and output handling properly, possibly as FreeRTOS tasks later on when I start work on the digital sequencer project again. But that is a job for another day.
If you are looking for reusable open hardware modules to speed up your bench testing, or need support designing custom breakout circuits, check out our catalog at Protoworks (NI).
- 2026-10-17 Refining the Protoworks service offerings Lab
- 2026-09-27 Belfast Tech Week Lab
- 2026-09-19 Refining the UCMOD Architecture R&D
- 2026-09-06 Giving AI Agents a Way to Talk to Protoworks Software
- 2026-09-03 A Morning at a Manufacturing Trade Show Lab
- 2026-09-01 Debugging 921600 Baud on the ATtiny1614 R&D