Skip to content
Level 3 · AdvancedBuildPart 17 · page 3 of 7240 minSafety level A · Standard home darkroomCraft££ Darkroom
240Minutes
7Chemicals
1Formulas
6Sources
ASafety level

Safety level A, standard home darkroom. Suitable with ordinary darkroom controls: nitrile gloves, eye protection, a well-ventilated room, dedicated utensils and correct labelling.

This page needs a darkroom. Where an alternative route exists it is given in the page's Alternative route section; the What you need page explains what can be improvised and what cannot.

Chemicals on this page7
Formulas on this page1

Build: The Timer Hardware

To build the half of the timer a printer actually touches, and to establish two things about it that cannot be established by looking: that every control can be found and identified by hand in the dark, and that the instrument’s own light does not fog the paper it will stand beside.

Nothing on this page computes an exposure. The firmware page does that, and the switched-output page drives the lamp. What comes out of this session is a working panel, an input-test log showing every control responding, and a processed sheet of paper with a control beside it.

By the end of this build you should be able to:

  • lay out a panel whose controls are distinguished by shape and position rather than by legend, and say why that is a design requirement rather than a nicety in a room with no light in it;
  • wire a detented rotary encoder, three buttons and a foot-switch jack to a Pico using its internal pull-ups, and explain what each pull-up is for;
  • decode quadrature with a transition table, and read the invalid-transition count as a measurement of contact bounce rather than guessing at it;
  • state the current budget for the whole instrument and where it comes from;
  • choose an output connector that cannot be confused with the foot-switch connector by touch;
  • run the fog test on your own instrument and read it against Kodak’s published criterion.

Part XIV’s electronics primer is assumed in full and is not repeated here: the Pico, GPIO, PWM, internal pull-ups, the breadboard-first rule, soldering technique and its controls, the current budget and the datasheet discipline all come from that page.

Part XVI’s fog test supplies the method used in Testing and calibration, and this page runs it on a new emitter rather than re-deriving it. The f-stop page explains why the encoder has to move in stops rather than in seconds.

Level A, and the criteria that put it there are the rubric’s own: purchased, certified equipment only, with no mains construction or modification anywhere in the build; substances limited to the ordinary print sequence in home-darkroom quantities for the fog test; and no heating of solutions.

What is not a hazard here, and why. There is no mains conductor in this enclosure at any point, so nothing on this page carries the shock or fire hazard that the isolation lesson exists to keep out of the course: the instrument is fed from a certified USB supply or a power bank and its highest potential is 5 V, which is inside the extra-low-voltage band HSE describe. Nor is there a chemical hazard in the electronics half of the session — a Pico, an encoder and a piezo are inert solids, and nothing here produces a vapour, so ventilation is not among this stage’s controls. The two hazards that are real are ordinary and specific: a soldering iron at around 350 °C, and the flux fume it makes. Both belong to Part XIV and its controls apply unchanged.

The fog test at the end brings the print chemistry back, at Level A, in the concentrations the developer and the fixer are supplied at, and nothing in it is stronger than the sequence Part XVI already ran.

Soldering. Burns from the iron and from freshly cut joints; the iron returns to its stand every time it leaves your hand. Rosin-based solder flux fume has a United Kingdom workplace exposure limit of 0.05 mg/m³ over eight hours and 0.15 mg/m³ over fifteen minutes, and it is annotated Sen — capable of causing occupational asthma. Part XIV’s extraction and ventilation controls are followed without modification; do not treat a short session as a reason to skip them, because sensitisation is not a dose-response you can feel accumulating.

Trimmed leads. Component legs fly when cut. Eye protection while cutting, and cut into your hand or into a bin rather than across the bench.

The foot-switch cable is a trip hazard, and this instrument lives in the dark. Route it under the bench, tape it down, and make it long enough to reach without being long enough to loop. A fall in a darkroom lands somebody among trays of chemistry and glass.

The instrument sits in a wet room. It goes on the dry side, away from trays, on a surface that does not get splashed, with its cables dressed so a drip runs away from a connector rather than into it.

Print chemistry for the fog test. Level A: nitrile gloves, eye protection, the usual ventilation for a print sequence, fixer collected for silver recovery.

Eye protection whenever leads are being cut and whenever solutions are handled. Nitrile gloves for the fog test’s three baths; they are not worn for soldering, where the hazard is heat rather than splash and a glove is a way of not noticing that you are being burned.

Ventilation is a control for the soldering stage and not for the rest: extraction at the iron under Part XIV’s rules, and the ordinary print-sequence ventilation for the fog test. It is not among the controls for the assembly and testing stages, because nothing there produces a vapour.

Part Quantity What it does Alternatives and notes
Raspberry Pi Pico 1 The instrument The course standard from Part XIV, so the pinout and firmware are shared
Rotary encoder, detented, with push switch 1 Sets every number 20 or 24 detents per turn; a bare potentiometer will not do, because the encoder is read as steps and not as a position
Tactile buttons, 12 mm 3 START, MODE, SET Different cap heights, so they differ by touch
Piezo transducer, passive 1 Metronome and confirmations Passive, not a self-driving buzzer: the firmware needs to choose the pitch
Display, red seven-segment, 4 digits 1 The number Or a monochrome I²C panel behind a red filter — see the display note below
Jack socket, 6.35 mm mono, switched 1 Foot switch The size is deliberate: nothing else on the panel accepts this plug
Output connector, 2-pin polarised 1 The low-voltage channel to the lamp Must not mate with the foot-switch plug; that is the whole specification
Resistors, 10 kΩ 2 Encoder pull-ups, if the internal ones prove insufficient Fitted only if the bounce test says so
Capacitors, 10 nF 2 Encoder RC filter Same condition
Capacitors, 100 nF and 10 µF 1 each Decoupling at the Pico The RP2040 datasheet asks for 100 nF close to each IOVDD pin
Enclosure, about 150 × 100 × 50 mm 1 Holds it, and stays put Diecast aluminium or ABS; see the no-soldering route below
USB supply or power bank, certified 1 Power HSE name battery-operated equipment as the safest choice where one will do
Stripboard, hook-up wire, heat-shrink The permanent build Or a breadboard, permanently, if that is the route you take

The display, and what this page will not tell you. The instrument stands a few tens of centimetres from unexposed paper, so its emission is a safelight question and is decided by the same evidence: Part XVI’s fog test, run on this emitter, with this paper. This course has not tested any particular display module against paper, so this page names no display as passing and none as failing. What it specifies is the design that makes a pass plausible and the test that decides it: red emission rather than white or blue, brightness under firmware control so the test can be run at the setting you will actually use, and a display the firmware can blank entirely. If your panel is a white-emitting one behind a red filter, the filter is part of the device under test and the test result belongs to the pair.

Temperature-controlled soldering iron and its stand, side cutters, wire strippers, tweezers, a helping hand, a multimeter, a small drill and a step drill or tapered reamer for the panel holes, a needle file, a scriber or centre punch, and a computer with a serial terminal. Nothing here is beyond the kit Part XIV’s builds already asked for.

Cost band ££. One named part costs more than all the rest together — the display — and the enclosure is the second line. Everything else is a handful of pounds of passives and connectors.

This page quotes no price for any of it, because the planner carries no electronics line at all; the parts for Parts XIV to XVII are among the gaps it names, and a figure invented here would be a guess dressed as a quotation. What outlives a quotation is the specification: a detented encoder with a push action, a passive piezo, a red-emitting display whose brightness can be commanded, and a certified low-voltage supply.

The one alternative that changes the total is the bought instrument, and that is priced: a commercial enlarger timer ran £112.20 to £429.00 on 5 September 2026. If your build lands near the bottom of that range, the honest comparison is not cost but what you get — a certificate you wrote yourself, and a log.

Almost nothing in the electronics half of this session is consumed; every component is capital and goes into an instrument meant to be recalibrated rather than replaced. The consumables are the fog test’s, and they are the same four the fog test page uses, at the smaller quantity a single-emitter test needs.

Consumed This session Sourced price Cost this session
Photographic paper, variable-contrast RC, 5 × 7 in 3 sheets — one range-finding, one test, one control £16.06 per 25 or £44.71 per 100, dated 5 September 2026, so £0.45–£0.64 a sheet £1.34–£1.93
Paper developer, working strength 500 mL of 1+9, that is 50 mL of concentrate £10.52 per 500 mL or £20.03 per 1 L, so £0.020–£0.021 a millilitre £1.00–£1.05
Stop bath, working strength 500 mL of 1+19, that is 25 mL of concentrate £10.66–£12.18 per 500 mL £0.53–£0.61
Fixer, working strength 500 mL of 1+4, that is 100 mL of concentrate £21.05–£25.98 per 1 L £2.11–£2.60
Lead-free solder, 0.7 mm about 2 g, or none on the breadboard route None. The planner carries no electronics line
Heat-shrink sleeving about 200 mm None. The planner carries no electronics line
Hook-up wire about 1.5 m None. The planner carries no electronics line
Stripboard one 50 × 100 mm piece, or none None. The planner carries no electronics line
Isopropanol, for flux residue a few millilitres The planner prices isopropanol only in Part II’s darkroom quantities
Wash water a few litres Not priced by the planner

The priced rows give £4.98 to £6.19 for one run of the fog test. That is a floor and not a total: five rows carry no dated price, nothing on them is free, and the bath figures assume the smallest useful volume is mixed and discarded after the session, which is what a controlled test requires.

Equipment is deliberately out of the table. The iron, the multimeter, the enclosure, the Pico, the display and the dishes are not consumed by a session, and a page that quietly counted them would stop measuring the thing the consumables calculator needs.

Eight stages, four hours. Stage 0 is the one that decides whether the instrument is usable, and it happens before anything is cut.

Stage 0 — Design the panel for a room with no light in it (30 minutes)

Section titled “Stage 0 — Design the panel for a room with no light in it (30 minutes)”

Sit at the enlarger, close your eyes, and reach for where the timer will be. That is the design session, and it produces four decisions.

Controls are identified by shape and position, never by a legend. In use you will not see the legends. So: the encoder is the only round thing, it is the largest thing, and it sits where the hand falls. START is the only large button and it is at the near edge, far enough from the others that a mistaken press is a deliberate act. MODE and SET are small, identical in function-class, and are distinguished from each other by height — one with a tall cap and one with a low cap — because the fingertip reads height instantly and reads position poorly.

Legends exist anyway, for the sighted first five minutes and for anybody else who uses the instrument. Engrave, label or print them large enough to read at arm’s length under a safelight, which in practice means about 4 mm of character height and high contrast, not small neat lettering.

The display faces you and nothing else. Recess it, hood it, or angle the enclosure so that its light goes to your eyes and not across the bench to the easel. This costs nothing at the design stage and cannot be retrofitted to a flat panel without a new panel.

Two connectors that cannot be swapped. The foot switch is a 6.35 mm jack; the output is a 2-pin polarised connector of a different family entirely. Not different colours — different shapes, because in the dark colour does not exist. Put them on opposite ends of the enclosure as well.

Draw the panel full size on paper, put your hand on the drawing, and only then mark the enclosure.

Stage 1 — Pico, encoder and buttons on a breadboard (40 minutes)

Section titled “Stage 1 — Pico, encoder and buttons on a breadboard (40 minutes)”

Breadboard first, always, and Part XIV’s reason stands: a breadboarded fault takes ten seconds to fix and a soldered one takes ten minutes and a wick.

The pin assignment below is the one the firmware assumes. It leaves GP4 and GP5 free for the display’s I²C, which is where the RP2 port puts I2C(0) by default, and it puts the output on GP15, which is the pin Parts XIV and XV already use for a switched load, so that the three instruments share one habit.

Signal Pin Wiring Why
Encoder A GP16 to encoder terminal A, internal pull-up Quadrature
Encoder B GP17 to encoder terminal B, internal pull-up Quadrature
Encoder push GP18 to encoder switch, internal pull-up Confirm and long-press
START GP19 button to 0 V, internal pull-up Pressed reads 0
MODE GP20 button to 0 V, internal pull-up Pressed reads 0
SET GP21 button to 0 V, internal pull-up Pressed reads 0
Buzzer GP22 100 Ω in series to the piezo, piezo to 0 V PWM drives it
Foot switch GP14 jack tip, internal pull-up; sleeve to 0 V Polarity learned at boot
Output GP15 to the switched-output page’s gate resistor Held low by its own pull-down
Display SDA / SCL GP4 / GP5 I²C, if a panel is fitted The port’s default assignment

Every button and every encoder contact goes to ground, with the Pico’s internal pull-up holding the line high when nothing is pressed. That is the arrangement Part XIV established, and the reason is worth restating in one line: a floating input reads as noise, and an input pulled to a defined level reads as a decision.

The interface board, laid out as it is wired

Pico1GP16GP17GP18GP19GP20GP21GP22GP14Encoder A B2pushSTART MODE SET3piezo, 100 Ω46.35 mm jack5one ground rail, joined at one pointout6GP15
  1. Pico, GP14 to GP22 — all inputs use the internal pull-up; GP15 is the only output
  2. Encoder A and B, with optional RC filter — fit the 10 kΩ and 10 nF only if the bounce test asks for them
  3. Three buttons to ground — START large; MODE tall cap; SET low cap
  4. Piezo through 100 Ω — bounds the pin current into a capacitive load
  5. 6.35 mm jack, foot switch — polarity learned at boot, not assumed
  6. 2-pin polarised output — a different family from the jack, at the other end of the box
Nine signals and two decoupling capacitors. The only thing on this board that can hurt an instrument is the connector choice, and that is decided at stage 0.

Stage 2 — Decode the encoder, and measure its bounce rather than guessing (40 minutes)

Section titled “Stage 2 — Decode the encoder, and measure its bounce rather than guessing (40 minutes)”

A quadrature encoder has two contacts that change in a fixed order. Read them as a two-bit number — call it A shifted left, plus B — and every legitimate movement is one of eight transitions: four one way and four the other. Everything else is either no movement or a bounce.

That fact is the decoder and the filter. A sixteen-entry table indexed by the previous state and the current one returns +1, −1 or 0, and a contact that chatters produces repeated same-state entries and occasional invalid ones, both of which return 0 and are therefore rejected by construction rather than by a delay.

Whether you also need hardware filtering is a measurement, not an opinion. The input test below counts the invalid transitions and prints them per detent. Turn the encoder slowly through twenty detents and read the number.

  • Zero or near-zero invalid transitions, and exactly four counts per detent: the internal pull-ups and the table are enough. Leave the resistors and capacitors out.
  • A scatter of invalid transitions, or counts that jump: fit the RC filter — 10 kΩ from each of A and B to 3V3 and 10 nF from each to ground, which gives a time constant of 100 µs, far longer than a contact bounce and far shorter than the fastest a hand can turn a knob.
  • Two counts per detent rather than four: your encoder rests between transitions rather than on them. Change the divisor in the firmware, not the wiring.

This course has measured no particular encoder, which is why the page gives the test and the three readings rather than naming a part and a figure.

Stage 3 — The piezo, and a pitch you have to choose (15 minutes)

Section titled “Stage 3 — The piezo, and a pitch you have to choose (15 minutes)”

A passive piezo transducer is, electrically, a small capacitor. Drive it from a GPIO through 100 Ω in series: the resistor bounds the current the pin has to supply while the element charges. The RP2040’s pads can be set to 2, 4, 8 or 12 mA of drive strength, defaulting to the second of those — and the datasheet is careful that those figures are not hard limits, but descriptions of how far the output voltage falls at a stated load. A pin asked for more than it likes does not shut down; it sags, and a sagging rail is a Pico that resets. The series resistor is what keeps the question from arising.

Use PWM rather than a square wave written in software, so the pitch is a number rather than a loop. Set the frequency to your transducer’s resonant frequency, from its datasheet. A piezo element is sharply resonant and its output falls away either side, so a device driven a kilohertz off resonance can be several times quieter for the same drive. The listing’s 2400 Hz is a provisional value, like every other provisional constant in this course, and it is meant to be replaced by yours.

How loud is loud enough is also a measurement. No sound-level figure for a piezo against the noise of running water and an extractor fan appears anywhere in this course. The test is the one that matters and it takes two minutes: run the metronome, stand where you print, turn the tap on, and see whether you can count by ear with your eyes shut.

Stage 4 — The foot switch, and the polarity you must not assume (25 minutes)

Section titled “Stage 4 — The foot switch, and the polarity you must not assume (25 minutes)”

A bought sustain pedal is the cheapest good foot switch there is, and it has one trap: pedals are made both normally-open and normally-closed, and many carry a polarity switch on the body that the previous owner may have left either way. Firmware that assumes “pressed reads 0” will treat a normally-closed pedal as permanently pressed.

The fix costs four lines of code and no hardware. At power-up the firmware samples the jack for a fifth of a second, takes whatever it finds as the released state, and treats any change from it as a press. The input test prints which polarity it inferred, so you can see that it inferred the right one by standing off the pedal while it boots.

Wire the tip to GP14 and the sleeve to 0 V. Use a switched jack socket if you have one, so the firmware can also tell whether a pedal is plugged in at all: with nothing inserted, the switch contact shorts the input to a known state and the instrument reports “no pedal” rather than “pedal held”.

The cable is the hazard, not the switch. Route it under the bench, tape it at two points, and leave no loop on the floor.

Stage 5 — Power, and the current budget (20 minutes)

Section titled “Stage 5 — Power, and the current budget (20 minutes)”

The whole instrument runs from USB: a certified supply with a moulded lead, or a power bank. HSE name battery-operated equipment as the safest choice where powered equipment is used, and a power bank is a lithium cell in a certified enclosure with its own protection circuit, which is precisely the difference between it and a bare cell.

The budget is short and worth writing down before you fit anything:

Load Typical draw Note
Pico, running tens of milliamps From the board datasheet’s own tables
Display, at full brightness the largest single line Read it from your module’s datasheet; a four-digit LED display multiplexed at full brightness can exceed the Pico itself
Piezo small and intermittent A capacitive load, not a continuous one
Encoder, buttons, foot switch negligible Pull-up currents only

Two rules follow. The Pico’s 3V3 pin is an output, and the board datasheet asks that its external load be kept below 300 mA — so a display drawing more than that gets its own feed from the 5 V rail and its own regulator, never the Pico’s. And whatever the total is, measure it: put the multimeter in series with the USB lead, run the instrument at full display brightness with the metronome going, and write the number on the inside of the lid.

Mark from the full-size drawing, centre-punch every hole, drill small and open out with a step drill or a reamer, deburr with a file. Fit the encoder last, because it is the part whose shaft length decides how the knob sits.

Two details that matter more than they look. The encoder knob wants to be big — 25 mm or more — because a large knob is easier to turn by a precise amount than a small one, and because it is the control your hand finds first. And the display’s window wants a bezel or a recess, not a flush sticker, so that its light is directed rather than scattered.

Stage 7 — Commit it, or do not (30 minutes)

Section titled “Stage 7 — Commit it, or do not (30 minutes)”

Two honest routes.

Solder it. Transfer to stripboard, cut the tracks, strain-relieve every wire that leaves the board with a cable tie or a hot-melt anchor, and label the connectors on the outside of the enclosure — the labels are for whoever opens it in two years, which is you.

Do not solder it. A breadboard screwed into the enclosure with the wiring dressed and the connectors brought out to the panel is a legitimate finished instrument, and it is the accessibility route for anybody who cannot solder. Part XIV said the same about its own builds. What you give up is reliability under vibration, which a bench instrument in a dark room does not experience much of. What you must not give up is strain relief: a wire pulled out of a breadboard in the middle of an exposure is the same fault as a wire pulled off a pad.

Save this as timer_inputs.py. It is not the timer — the firmware page has that — it is the smallest program that proves every control is wired correctly, and it is the only thing you should run until it passes.

"""
timer_inputs.py - Pure Silver, Part XVII: input test for the timer hardware.
Target Raspberry Pi Pico (RP2040), MicroPython v1.24.1 for RPI_PICO.
Install copy to the board as timer_inputs.py, then at the REPL:
import timer_inputs as t
t.run()
Licence CC BY-SA 4.0, as the rest of this course's content.
Everything printed is comma-separated, with '#' beginning any line that is a
comment, so a captured terminal session is already the build log this page
asks you to keep.
This program never asserts the output pin. It is an input test, and the lamp
channel stays off throughout - which is why it is safe to run with the head
connected.
"""
import machine
import micropython
import sys
import time
micropython.alloc_emergency_exception_buf(100)
VERSION = "1.0"
# --- Wiring. These nine numbers are the whole hardware interface. -----------
PIN_ENC_A = 16
PIN_ENC_B = 17
PIN_ENC_SW = 18
PIN_START = 19
PIN_MODE = 20
PIN_SET = 21
PIN_BUZZER = 22
PIN_FOOT = 14
PIN_OUT = 15
DEBOUNCE_MS = 25 # further edges on a button ignored for this long
FOOT_SETTLE_MS = 200 # the jack is sampled for this long at boot
PIEZO_HZ = 2400 # provisional: use your transducer's resonant frequency
PIEZO_DUTY = 32768 # half of 65535, a square wave
COUNTS_PER_DETENT = 4 # 2 on encoders that rest between transitions
# Quadrature transition table, indexed by (previous << 2) | current, where a
# state is (A << 1) | B. The eight legal transitions give +1 or -1; the four
# impossible ones and the four no-change ones give 0, so a bouncing contact is
# rejected by the table itself rather than by a delay.
_STEP = (0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0)
enc_a = machine.Pin(PIN_ENC_A, machine.Pin.IN, machine.Pin.PULL_UP)
enc_b = machine.Pin(PIN_ENC_B, machine.Pin.IN, machine.Pin.PULL_UP)
enc_sw = machine.Pin(PIN_ENC_SW, machine.Pin.IN, machine.Pin.PULL_UP)
btn_start = machine.Pin(PIN_START, machine.Pin.IN, machine.Pin.PULL_UP)
btn_mode = machine.Pin(PIN_MODE, machine.Pin.IN, machine.Pin.PULL_UP)
btn_set = machine.Pin(PIN_SET, machine.Pin.IN, machine.Pin.PULL_UP)
foot = machine.Pin(PIN_FOOT, machine.Pin.IN, machine.Pin.PULL_UP)
out = machine.Pin(PIN_OUT, machine.Pin.OUT, value=0)
buzzer = machine.PWM(machine.Pin(PIN_BUZZER))
buzzer.duty_u16(0)
_counts = 0 # quadrature counts, four per detent on most encoders
_bad = 0 # transitions the table calls impossible: bounce
_state = (enc_a.value() << 1) | enc_b.value()
_foot_released = 1 # replaced at boot by whatever the jack actually reads
def _on_quadrature(pin):
"""Hard-IRQ safe: no allocation, no printing, table lookup only."""
global _state, _counts, _bad
now = (enc_a.value() << 1) | enc_b.value()
step = _STEP[(_state << 2) | now]
if step:
_counts += step
elif now != _state:
_bad += 1
_state = now
enc_a.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,
handler=_on_quadrature, hard=True)
enc_b.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING,
handler=_on_quadrature, hard=True)
def beep(ms=40, hz=PIEZO_HZ):
"""One confirmation tone. Blocking, and short enough not to matter here."""
buzzer.freq(hz)
buzzer.duty_u16(PIEZO_DUTY)
time.sleep_ms(ms)
buzzer.duty_u16(0)
def learn_foot_polarity():
"""Take whatever the pedal reads at boot as its released state.
Sustain pedals are made both normally open and normally closed and many
carry a polarity switch, so 'pressed reads 0' is an assumption this course
will not make. Stand off the pedal while this runs.
"""
global _foot_released
samples = []
end = time.ticks_add(time.ticks_ms(), FOOT_SETTLE_MS)
while time.ticks_diff(end, time.ticks_ms()) > 0:
samples.append(foot.value())
time.sleep_ms(5)
_foot_released = 1 if sum(samples) * 2 > len(samples) else 0
return _foot_released
def foot_pressed():
return foot.value() != _foot_released
def detents():
"""Encoder position in detents, signed."""
return _counts // COUNTS_PER_DETENT
def selftest():
"""Report the resting state of every input. Nothing here moves the output."""
print("# Pure Silver timer input test v{}".format(VERSION))
print("# {}".format(sys.implementation))
released = learn_foot_polarity()
print("# foot switch released state learned as {}".format(released))
print("# resting: enc_a {} enc_b {} enc_sw {} start {} mode {} set {}".format(
enc_a.value(), enc_b.value(), enc_sw.value(),
btn_start.value(), btn_mode.value(), btn_set.value()))
if 0 in (btn_start.value(), btn_mode.value(), btn_set.value()):
print("# FAIL: a button reads pressed with nothing touching it.")
print("# Check for a short to ground, or a button wired across 3V3.")
return False
if out.value():
print("# FAIL: the output pin is high at rest.")
return False
beep()
print("# PASS: every input rests high and the output rests low.")
print("# If you heard nothing, the piezo, its resistor or PIEZO_HZ is wrong.")
return True
def run(seconds=120):
"""Log every input event for `seconds`, then print the bounce figure.
Work through the panel with your eyes shut: turn the encoder twenty
detents each way, press every button, press the encoder, and use the
pedal. Every event should appear once, in the order you made it.
"""
global _bad
if not selftest():
return
_bad = 0
start_counts = _counts
print("t_ms,event,detail")
buttons = ((btn_start, "START"), (btn_mode, "MODE"),
(btn_set, "SET"), (enc_sw, "ENC_PUSH"))
last = {name: 1 for _, name in buttons}
last_edge = {name: 0 for _, name in buttons}
last_foot = False
shown = detents()
end = time.ticks_add(time.ticks_ms(), seconds * 1000)
while time.ticks_diff(end, time.ticks_ms()) > 0:
now = time.ticks_ms()
here = detents()
if here != shown:
print("{},ENCODER,{:+d}".format(now, here - shown))
shown = here
beep(8)
for pin, name in buttons:
v = pin.value()
if v != last[name]:
if time.ticks_diff(now, last_edge[name]) < DEBOUNCE_MS:
continue
last_edge[name] = now
last[name] = v
print("{},{},{}".format(now, name, "down" if v == 0 else "up"))
if v == 0:
beep()
f = foot_pressed()
if f != last_foot:
last_foot = f
print("{},FOOT,{}".format(now, "down" if f else "up"))
if f:
beep()
time.sleep_ms(2)
turned = abs(_counts - start_counts)
print("# {} quadrature counts, {} rejected as impossible".format(turned, _bad))
if turned:
print("# {:.2f} bad transitions per detent".format(
_bad / (turned / COUNTS_PER_DETENT)))
print("# a figure near zero means the table is filter enough;")
print("# a scatter means fit the 10k and 10n on A and B and run this again.")
buzzer.duty_u16(0)

Three things in that listing are worth a second reading. The interrupt handler is registered with hard=True and allocates nothing, because a hard interrupt handler that allocates will fail at the worst possible moment; everything it does is a table lookup and two integer operations. The foot switch’s polarity is learned rather than assumed. And the output pin is created with value=0 and never touched again, so this program can be run with the lamp channel connected.

Three tests, in this order. The first two are done in the light; the third is the reason the page declares a darkroom.

T1 — Every control responds, once, in the order you pressed it

Section titled “T1 — Every control responds, once, in the order you pressed it”

Run t.run() and work the panel with your eyes shut. Read the log afterwards and check four things: every event you made appears; no event appears that you did not make; a single press produces a single down and a single up; and the encoder’s sign matches the direction you turned it. If it counts backwards, swap A and B — in the wiring or in the two pin constants, whichever is easier.

Then read the bounce figure at the end of the log and act on it as stage 2 says. Record the number in your build log: it is a property of the encoder you bought, and it is the first line of this instrument’s certificate.

T2 — Legible and findable at the working position

Section titled “T2 — Legible and findable at the working position”

In the dark, at the enlarger, with your hands where they will be:

  • Can you find START without hunting, and are you confident you have not found MODE?
  • Can you read the display from the working position at the brightness you intend to use?
  • Does every accepted action produce a sound you can hear over the extractor and the tap?
  • Can you turn the encoder by exactly three detents by feel?

A “no” to any of these is a panel problem and it is cheaper to fix now than after the fog test.

The instrument is an emitter standing next to unexposed paper, and Kodak’s own guide makes the point that matters: virtually all exposures are cumulative, so the display’s contribution adds to the safelight’s rather than being separate from it. An instrument that passes on its own and pushes the room’s total over the line has still failed.

Run Part XVI’s fog test procedure unchanged, with one substitution: the light under test is the timer, running, at the brightness and distance you will actually use, in the position it will actually occupy. Follow that page for the grey-tone pre-exposure, the stepped exposure, the After and Before patches and the processing in total darkness, and read the result against its criterion — a safe time not more than half the time at which a change first becomes detectable.

Then run it again with the safelight on as well, because that is the condition you print in, and the cumulative rule means the pair has to be tested as a pair.

Three outcomes and what each means.

  • No detectable change at the longest step, alone and with the safelight: record the time, the brightness setting and the distance, and treat the instrument as tested at those settings only.
  • A change appears: turn the brightness down, move the instrument further away or hood it, and retest. Distance is the cheapest of the three because it costs nothing to try.
  • A change appears at the lowest usable brightness: the display is not suitable and the instrument goes outside the room, or behind a shutter that the firmware closes during an exposure. Write that down as a result rather than as a failure — it is exactly the finding this test exists to produce.

Enter all three tests on the instrument’s certificate, with the date. The calibration page adds the timing figures to the same sheet later.

Without a darkroom. The build itself needs none: stages 0 to 7 and tests T1 and T2 are bench work in daylight. The fog test is not substitutable, because the quantity it measures is light in a room. What a reader without a dark room should do is small and honest: keep paper in a changing bag until the moment of exposure, use a daylight tank for film, and record on the certificate that the instrument’s emission is untested — which is a different statement from “acceptable”, and the difference is the point. Part XVI’s room page lists the temporary blackouts that turn “no dark room” into “a dark room for two hours”, and two hours is more than this test needs.

Without soldering. Stage 7’s breadboard route is a complete build, and every test on this page applies to it unchanged.

Without buying an enclosure. A wooden box, a rigid plastic tool case, a biscuit tin — anything rigid, opaque and stable will do, provided the panel can be drilled accurately and the box does not slide on a dry bench. Opaque matters more than pretty: a translucent enclosure with an LED inside it is a lamp.

Without building at all. A bought enlarger timer, £112.20 to £429.00 on 5 September 2026, is a legitimate route through this whole part, and every test on this page still applies to it. A commercial instrument’s display has not been fog-tested in your room either.

Symptom Likely cause Test that separates it
A button reads pressed at rest Wired to 3V3 rather than to ground, or a solder bridge selftest() names the pin; a meter across the button reads 0 Ω at rest
The encoder counts in the wrong direction A and B swapped Swap the two constants; nothing else changes
The encoder counts two per detent, or eight The detent rests between transitions, or the table is being entered twice Set COUNTS_PER_DETENT; the count per detent is printed by the test
Many “impossible” transitions Contact bounce beyond what the table absorbs Fit the 10 kΩ and 10 nF filter and rerun; the figure should fall to near zero
The pedal reads permanently pressed Normally-closed pedal, or your foot was on it at boot Reboot with your foot off it; selftest() prints the polarity it learned
No sound at all Wrong pitch for the transducer, an active buzzer fitted instead of a passive one, or the 100 Ω open Sweep the frequency by hand from 1 to 5 kHz and listen for the peak
Sound too faint at the easel Driven off resonance, or the piezo is buried inside the enclosure Drill a hole over the disc; that alone is often several times louder
The display flickers or the Pico resets Display drawing more than the 3V3 pin’s 300 mA budget Meter the supply current; give the display its own regulated feed
The instrument slides on the bench when you press START Nothing wrong electrically Feet, or a heavier enclosure; a control you have to hold still is a control you will mis-press
  1. Your input test reports four counts per detent and 0.02 impossible transitions per detent. Say what you fit, and what you would have fitted at 1.5 per detent, and why the second number is not simply “worse wiring”.
  2. A friend proposes labelling the three buttons with coloured caps instead of different heights. Explain, from the conditions this instrument is used in, why that fails, and name the one situation in which colour would still be worth adding.
  3. The foot switch reads permanently pressed when a particular pedal is plugged in, and correctly with another. Give the mechanism, and say why the firmware’s answer is better than a solder-blob fix.
  4. Your display module’s datasheet gives a maximum current of 340 mA at full brightness. State exactly what you may not do, quoting the number from the board datasheet that decides it, and give two arrangements that would be acceptable.
  5. The fog test passes with the timer alone and fails with the timer and the safelight together. Interpret that result, and say which of the two you would change first and why.
  6. Explain why the input-test firmware creates the output pin with value=0 and then never touches it, and what would go wrong if it toggled the pin to prove that it worked.
  7. You cannot solder. Set out the route through this page that gets you a working instrument, and name the one thing you must not compromise on.

Measure your encoder’s bounce properly. Put a second Pico on the A line, timestamp every edge with ticks_us, and turn the knob at three speeds. The distribution of inter-edge intervals is the bounce figure that this page could only estimate, and it tells you whether the RC filter’s 100 µs is generous or marginal for your part.

Find the piezo’s resonance by measurement rather than datasheet. Sweep the PWM frequency from 1 to 6 kHz in 100 Hz steps, hold each for a second, and record the loudest by ear at the working position — then repeat with the enclosure closed. The difference between the two is what a hole over the disc is worth.

Test the display’s emission against the safelight’s. Both are emitters in the same room, and Part XVI’s spectra page gives the framework. Running the fog test on the display alone, the safelight alone and the two together produces three numbers whose relationship is the cumulative-exposure rule made visible.

Try the panel on somebody else. Hand the instrument, in the dark, to a person who did not build it, and ask them to set 12.4 seconds and start an exposure. Everything you learn in the next two minutes is about the design and not about them.

Check your understanding

Question 1. The input test prints "4 quadrature counts per detent, 1.8 bad transitions per detent". What does the second figure mean and what do you do?
Show the answer and why

Answer: Contacts are chattering enough that the transition table is rejecting real movement as well as noise; fit the 10 kΩ pull-ups and 10 nF capacitors on A and B and run the test again

A "bad" transition is one the table calls impossible — the state changed by two bits at once, which quadrature cannot do — and it means the contacts are bouncing fast enough that the interrupt sees an intermediate state. Nearly two per detent is a scatter rather than an occasional event, so the hardware filter earns its place: 10 kΩ and 10 nF give a 100 µs time constant, far longer than a bounce and far shorter than a hand. Raising a button debounce interval would not help, because the encoder is not debounced by time. And the last option is the tempting one: the table does reject them, but a rejected transition can also lose a real step, so the count itself is no longer trustworthy.

Question 2. Why does this page insist that the foot-switch connector and the output connector be different shapes rather than different colours?
Show the answer and why

Answer: Because the instrument is used in a room with no light in it, where colour does not exist and shape is the only property a hand can read

It is the same rule as the panel layout: in the dark, colour conveys nothing and shape conveys everything. The consequence of getting it wrong is not cosmetic — the foot-switch line is an input with a pull-up and the output line drives a transistor gate, so plugging one into the other connects a lamp channel to a pedal. Current rating is not the issue at these levels, and the firmware genuinely cannot tell, which is exactly why the connector has to.

Question 3. Kodak's darkroom guide states that virtually all exposures are cumulative. What does that require of this page's fog test?
Show the answer and why

Answer: That the timer be tested on its own and again with the safelight running, because the condition you print in is the two together and a pass on one is not a pass on the pair

Cumulative means the paper adds what it receives from every source, so the quantity that matters is the total in the room during the time the paper is out. An instrument that passes alone can still push a room that was already near its limit over it. Adding the contributions arithmetically would need each in the same units, which a visual fog test does not give you. Switching the safelight off is not a control, it is giving up the ability to work; and doubling the duration tests a different question from the one asked.

Question 4. Your display module draws 340 mA at full brightness. What does the Pico datasheet say about that, and what are your options?
Show the answer and why

Answer: The 3V3 pin is an output whose external load should be kept below 300 mA, so the display must not be fed from it; either run it at a brightness that keeps the total inside the budget, or give it its own regulator from the 5 V rail

The constraint is on the Pico's own 3.3 V regulator output rather than on the incoming USB supply, and the board datasheet states the 300 mA figure directly. Two arrangements satisfy it: reduce the demand, which the firmware can do because brightness is under its control, or take the display off the Pico's regulator entirely and feed it from VBUS through a regulator of its own. The 12 mA figure is the pad drive strength, which describes how far a single GPIO's output voltage sags at a stated load rather than being a ceiling it enforces, and is a different quantity entirely. Replacing the board is a solution to a problem you have not got.

Question 5. The input-test firmware registers its encoder interrupt with hard=True and does nothing in the handler but a table lookup. Why both?
Show the answer and why

Answer: Because a hard-interrupt handler runs in a context where allocating memory will fail, so the handler must do no allocation — and a table lookup with integer arithmetic allocates nothing, while formatting a string or appending to a list would

The two go together: hard interrupts minimise the delay and jitter that would otherwise let a fast encoder edge be missed, and the price is that the handler may not allocate. That is why the handler updates three module-level integers and returns, and why every print in the program happens in the main loop. Soft interrupts do exist on this port and are what the timer firmware uses elsewhere; and the encoder produces plenty of events — four per detent, at whatever speed a hand can turn.

Sources for this page

6 cited · checked 2026-09-05

  1. 01Raspberry Pi Pico Datasheet: An RP2040-based microcontroller boardRaspberry Pi Ltd§ Section 1, key features - 26 multi-function 3.3 V GPIO of which 0 to 22 are digital only and 26 to 28 may also be ADC inputs, 16 PWM channels; section 2.3 and the pin descriptions - VBUS is the 5 V from the USB connector, VSYS accepts 1.8 to 5.5 V, and the 3V3 pin is an output whose external load is to be kept below 300 mA; section 4.2, general purpose IO, powered from the on-board 3.3 V rail and fixed at 3.3 Vdatasheets.raspberrypi.com/pico/pico-datasheet.pdftier 1, primary2026-09-05
  2. 02RP2040 Datasheet: A microcontroller by Raspberry PiRaspberry Pi Ltd§ Section 2.19, GPIO - output drive strength can be set to 2 mA, 4 mA, 8 mA or 12 mA, with the pad control register's DRIVE field defaulting to 0x1; section 5.5.3.5, interpreting GPIO output voltage specifications, where those figures are stated not to be hard limits but to describe how far the output voltage falls at a stated load; section 2.9.1, digital IO supply, which asks for a 100 nF decoupling capacitor close to each IOVDD pindatasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdftier 1, primary2026-09-05
  3. 03Quick reference for the RP2, MicroPython documentationDamien P. George, Paul Sokolovsky and contributors§ Pins and GPIO - the internal pull-up on an input, and Pin.irq with its trigger constants; PWM - eight slices of two channels each, sixteen channels in total; hardware I2C, where machine.I2C(0) takes the port's default assignment of scl on Pin 5 and sda on Pin 4docs.micropython.org/en/latest/rp2/quickref.htmltier 1, primary2026-09-05
  4. 04How Safe Is Your Safelight? A Guide to Darkroom Illumination, publication K-4Eastman Kodak Company, 2006§ Test for Black-and-White Papers - the grey-tone pre-exposure targeted at a reflection density of 0.25 to 0.50, the stepped exposure made with a moving card, the After and Before enlarger exposures at opposite ends of the sheet, processing in total darkness, the term super-additive exposure with the statement that virtually all exposures are cumulative, and the definition of safe time as an exposure less than or equal to one half of the time required to produce a detectable changekodak.com/content/products-brochures/Film/KODAK-A-Guide-to-Darkroom-Illumination-K-4.pdftier 1, primary2026-09-05
  5. 05EH40/2005 Workplace exposure limits, containing the list of workplace exposure limits for use with COSHHHealth and Safety Executive, 2005§ Table 1 - rosin-based solder flux fume, CAS 8050-09-7, at 0.05 mg/m3 over eight hours and 0.15 mg/m3 over fifteen minutes; Annotations - Sen, capable of causing occupational asthmahse.gov.uk/pubns/priced/eh40.pdftier 1, primary2026-09-05
  6. 06Electrical safety and you: A brief guide, INDG231(rev1)Health and Safety Executive, 2012§ Reducing the risk, Reduce the voltage - limit the supply voltage to the lowest needed to get the job done, with battery-operated equipment named as the safest choicehse.gov.uk/pubns/indg231.pdftier 1, primary2026-09-05

Formulas, hazard statements, historical dates and process descriptions on this page were checked against the sources above on the date shown. Safety data changes: obtain the current safety data sheet for the product you actually buy before you open it.