Modules
A Module is a component you write yourself: a class whose build() creates a few shop parts and wires them together, and whose expose() publishes the pins the outside world may use. The board draws the module as one group, and it costs the sum of its parts — each part inside is bought normally.
from wiring import *
pi = Pi()
class TwoLEDs(Module):
"""Two LEDs, each with its own resistor, sharing a ground."""
def build(self, color="red"):
self.r1 = Resistor(330)
self.r2 = Resistor(330)
self.d1 = LED(color)
self.d2 = LED("green")
self.r1 >> self.d1
self.r2 >> self.d2
self.d1.cathode >> self.d2.cathode # the shared ground
self.expose(sig1=self.r1.pin1, sig2=self.r2.pin1, gnd=self.d1.cathode)
self.chain("sig1", "gnd") # pi.GPIO17 >> lights >> pi.GND
lights = TwoLEDs("red")
pi.GPIO17 >> lights >> pi.GND
connect(pi.GPIO27, lights.sig2)from gpiozero import LED
from time import sleep
one = LED(17)
two = LED(27)
while True:
one.on()
two.off()
sleep(1)
one.off()
two.on()
sleep(1)build()
build() is where the parts exist. It receives whatever you passed to the constructor — TwoLEDs("red") arrives as color="red" — so a module can be parameterised like any part:
indicator = TwoLEDs("blue", label="panel")
Inside build() you use the normal constructors, so the parts are bought, laid out and grouped like any others. The parts become attributes on self (self.d1) and also stay in the build; either way of reaching them works.
A module that never defines build() raises NotImplementedError when it is created, and one whose build() creates no parts raises a CircuitError.
expose()
self.expose(name=pin, …) publishes inner pins under names you choose. Exposed pins can be reached as attributes, by pin(...), and by brackets — lights.gnd, lights.pin("gnd") and lights["gnd"] are the same Pin:
pi.GPIO17 >> lights.sig1
lights.gnd >> pi.GND
Every value passed to expose() must be a Pin; anything else is a CircuitError. lights.pin_names() lists the exposed names, in the order they were published.
chain()
self.chain(near, far) names two exposed pins that a chain passes through. After self.chain("sig1", "gnd"), pi.GPIO17 >> lights wires GPIO17 to sig1 and returns gnd, so a whole module reads as one link in the chain.
A module without a chain cannot be a chain link: pi.GPIO17 >> lights raises the same "more than two pins" error as any many-pin part, and lights >> pi.GND raises one telling you to call self.chain(near, far) in build(). Wire an exposed pin instead in that case.
Reusing a module in other files
A module is ordinary Python, so it can live in its own file. Every project file is importable by name, and circuit.py runs with the project folder on the path, so from traffic import TrafficLight works:
from wiring import *
class TrafficLight(Module):
def build(self):
self.red_led = LED("red")
self.yellow_led = LED("yellow")
self.green_led = LED("green")
self.r1 = Resistor(330)
self.r2 = Resistor(330)
self.r3 = Resistor(330)
self.r1 >> self.red_led
self.r2 >> self.yellow_led
self.r3 >> self.green_led
self.red_led.cathode >> self.yellow_led.cathode
self.yellow_led.cathode >> self.green_led.cathode
self.expose(red=self.r1.pin1, yellow=self.r2.pin1, green=self.r3.pin1, gnd=self.red_led.cathode)
from wiring import *
from traffic import TrafficLight
pi = Pi()
lights = TrafficLight()
connect(pi.GPIO17, lights.red)
connect(pi.GPIO27, lights.yellow)
connect(pi.GPIO22, lights.green)
connect(lights.gnd, pi.GND)
Keep the module class at the top level of its file: defining the class buys nothing, and only creating an instance builds its parts.
To reuse a module in every build, including the daily contest, save it as a custom component: it goes into your library and any workspace can import it.
Pricing
A module costs the sum of its parts: each part created in build() is bought at its shop price, just as if you had written it in circuit.py. The module itself is not a shop item. The built-in kits.* modules are the other way round — one pre-wired item with its own price — see Kits.
Going deeper
- Modules nest. A
build()may create another module; the board draws the outermost module as the group around them. label=names the group on the board and in error messages:TwoLEDs("red", label="panel")shows aspanel.- Exposed pins are the module's API. Anything not exposed is still on the board and wired, but outside code reaches it through the part attributes only if you keep them (
lights.d1) — expose what a user of the module should touch. - Errors to expect: a build that makes no parts,
expose()without a pin, an unknown exposed pin, a missing chain. All of them are listed on Circuit errors.