Skip to the content
2 layers100 × 100 mmKijaniCarrier, the printed circuit board in 3D

KijaniCarrier

Waziup/WaziDev5

WaziDev official repository

Size
100 × 100 mm
Copper layers
2
Thickness
1.6 mm
Parts
75
Nets
44
Pads
198
Tracks, vias
320, 55
Pours
8
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
24 Nov 2019
Updated
5 Nov 2026

Overview

KijaniCarrier is an open-source printed circuit board by Waziup, from the Waziup/WaziDev repository on GitHub, published under the Apache-2.0 licence. It is a 2-layer board of 100 × 100 mm with 75 parts on 44 nets, routed with 320 tracks and 55 vias.

It is built around the THVD1406D, DS18B20 and MCP1700-3302E/TT, with the TPS61085, AO3401A and SM712 among its other parts. Its parts include 16 capacitors, 16 resistors, 8 ICs, 8 connectors and 6 transistors.

Main components

8

README

examples/LoRaWAN/Water_monitoring/WaterSensorNode/pcb/revC/README.md

KijaniSpace Carrier — PCB

Start at ../../README.md: the project overview, all guides in the order to use them, and how to proceed. This file covers the board itself: generator, checks, fabrication files.

Generated for KiCad 10.0.6. Open KijaniCarrier.kicad_pcb.

What is in here

FileWhat it is
KijaniCarrier.kicad_pcbThe board: 100 × 100 mm outline, the isolation slot, 4 mounting holes, 56 footprints placed
KijaniCarrier.kicad_proProject file
KijaniSpace.pretty/The five footprints nobody else has: EZO circuit, BE-IVI isolator, Pololu U3V9F12, MAX3485 module, and the gangable KF350-3.5 2-pole terminal
out/FACTS.mdThe generated ground truth. Counts, the BOM, the order settings. Where any document disagrees with it, the document is wrong. python gen/facts.py
out/drc.txtThe DRC report. bash gen/views.sh
out/view.svgFront copper + silkscreen + outline, for a quick look. bash gen/views.sh
out/KijaniCarrier-top-1zu1.pdfThe 1:1 print for the paper check, at true scale on A4. bash gen/views.sh
out/footprints/One SVG per custom footprint, five of them. bash gen/views.sh
out/fab/The current fabrication files, rebuilt from scratch by bash gen/fab.sh

Before you open it: register the library

KiCad does not yet know where KijaniSpace.pretty lives, which is why the DRC report carries 11 library warnings. They are configuration, not design faults.

Preferences → Manage Footprint Libraries → Project Specific Libraries → add KijaniSpace.pretty from this folder, nickname KijaniSpace.

Pad order: mirrored on 2026-10-06

The first version had every module's pad row reversed. It was caught by laying a module on the table exactly as it will sit on the board — component side up, pins down — and reading the silkscreen left to right. The board now matches:

Modulerowleft → right
BE-IVI isolatorMCU side (top)VCC OFF GND A B
isolated side (bottom)3.9V N/C GND A B
EZO circuitbus side (top)GND TX RX
probe side (bottom)VCC PRB PGND
Pololu U3V9F12single rowVIN GND VOUT
MAX3485 modulesingle rowVCC TXD RXD GND

The Pololu row is the one entry not confirmed against a photo. Its pin labels are printed on the back of the module, so reading them gives the mirrored order; that is why it was flipped along with the rest. Check it the same way before soldering. VCC and GND swapped destroys the transceiver and both EZO circuits on first power-up.

Each custom footprint carries a 1 mm silkscreen label naming pin 1 — 1:VCC, 1:GND, 1:VOUT — beside the square pad. That is the marking to check the real module against.

State: fully routed, and clean once the zones are filled

With the pours filled, KiCad reports 0 unconnected, 0 electrical errors and 0 silkscreen errors. The only thing left is 11 lib_footprint_mismatch warnings: KiCad compares the board's embedded footprints against the library and the generator writes rotation differently than pcbnew would. The Gerbers come from the board, not the library, so this does not reach the fabricator. Do not run "Update Footprints from Library" — that is the one action that could act on the difference and move pads.

Seven reference designators sat on a neighbour's silkscreen, on a pad, or in the isolation slot, and the two isolator outlines ran straight across the slot; REFPOS in gen/mkpcb.py places those seven, and the isolator footprint now breaks its outline where the slot passes. One of them was less obvious than the rest: the DRC also checks a footprint's reference against its own graphics, and C1's had been sitting inside its own body circle.

253 track segments and 37 vias on 36 nets. kicad-cli on the unfilled file still shows GND as unconnected and five vias as dangling, because it does not fill zones. Fill and it goes to zero: gen/fill2.py does exactly that from KiCad's own Python, writes filled.kicad_pcb, and running the DRC on that is how the state above was measured.

What the ground plane hid

Five GND pads could not actually reach the pour, and the plane itself was in pieces. None of it is visible before the fill.

  • Q2.2, R3.2 and C6.2 sat in pockets walled in by the traces around them, with half a millimetre between two keepouts as the only way out.
  • U1.2, the Pololu's ground pin, is boxed in between its own two neighbours on both layers. It now leaves on the back through the gap between the V12IN and +12V runs.
  • The front-layer pour below y=67 breaks into six islands. One of them carries Q4.2 and R6.2 and hung on nothing at all; another was the only copper under half the bottom edge. Each island that carries a pad now has its own stitching via.
  • SWGND reached the isolators from the transceiver only through the SWGND pour, which the RS485 descents cut apart. It is a trace now.

Two pads also take the pour solid instead of through a thermal relief (gen/zonecon.py): C4.2, where three of four spokes are blocked, and U1.2, whose front-layer spokes all ran into a dead island.

gen/isl.py lists the filled islands; gen/pour.py floods the pour from one GND pad. pour.py found the first three pockets. It missed U1.2 — its raster counted a 0.44 mm squeeze beside the pad as copper, where KiCad's filler, with min_thickness and island removal, does not. The authority is the filled board, not the raster.

The pull-up resistors moved

R11 and R12 were at (60, 78) and (65, 78), where their links to the I2C bus ran up the middle of the board at x 56.5 and 57.5 and blocked the only corridor the RS485 bus could use. They now sit at (84, 50) and (84, 46), rotated 180°, directly on the SDA and SCL runs. The stubs are a few millimetres instead of forty, which suits an I2C bus better than the old position did.

The alternative was to move J8 about 8 mm west. That was rejected: J8 is a screw terminal with a cable on it, so moving it changes mechanics that were already reviewed, while two 0603 resistors change nothing but copper.

The routing discipline

Nets run in named lanes so two of them never need to cross on the same layer. Down the left edge, on the back:

xnet
21.5SWGND
24.53V3P
27.53V3REG
29.5A1
38.5OW_DATA
51.5ISO_VIN

The four MCU-side nets reach the isolators' top row in a fixed order, each dropping onto its own pad before continuing east to the second isolator in back-layer lanes at y 63 (SWGND), 65 (SDA) and 67 (SCL) — all north of the isolation slot at y 68.8. The RS485 pair comes down the front layer in two columns at x 67 and x 72.5, both west of the pH slot at x 74, so neither crosses a barrier.

Checks, run on every regeneration (gen/build.sh)

  • barrier.py — no track and no zone crosses either isolation slot. This is the one thing the board exists for.
  • check.py — endpoints on their own net's pads, pad clearance, track-to-track clearance per layer, via clearance, and the barrier again. This caught every crossing during routing; the DRC run only ever confirmed it.
  • pour.py — can the filled ground actually reach every GND pad.
  • endpoints.py — the narrower endpoint check, kept because it is what found the stale coordinates after the pad order was mirrored.
  • kicad-cli pcb drc

Route points are written as 'REF.PAD' wherever they touch a pad, so a coordinate cannot be mistyped — an earlier pass transcribed them from a one-decimal printout and every endpoint missed its pad.

Full review before the first board

Checked, with the method, not by eye:

Result
Netlist against the firmware's pin assignmentsmatches, no firmware change needed
Every pad's net, every net's pad count36 nets, no net with a single pad
Pads with no net8: the four mounting holes and the isolators' OFF and N/C, all correct
Trace width against current (IPC-2152, 1 oz, 10 K rise)widest requirement 0.12 mm, narrowest power trace 0.50 mm
Via annular ring0.20 mm (JLCPCB minimum 0.13)
Drill sizes0.40 … 3.20 mm (minimum 0.30)
Board outlineclean 100 × 100 mm, two closed 20 × 2.4 mm slots
Placementall 56 footprints on the front — single-sided assembly
Gerber set2 layers, 1.6 mm, 11 files correctly typed, one drill set, slots in the profile
BOM against the boardall 56 footprints covered by 60 BOM designators (the four module positions are listed as their eight socket rows), none spare, none twice
Isolationno track, via or pour crosses either slot
Filled ground0 unconnected, every GND pad reachable

The netlist check is the one that matters most, because no DRC can catch a wrong net. RAIL12_EN = 5 meets J5B.1 (D5), ISO_EN = A1 meets J3.2, ONE_WIRE_PIN = A2 meets J3.1, and RS485 TX/RX meet D4/D3 the right way round — U3.2 is the module's TXD and goes to D4, which the firmware drives as its transmit pin.

Three things to check on the test board, not on the screen

1. The isolator supply is the raw cell. ISO_VIN comes off Q3's drain, whose source is +VBATT — so the isolators see 3.0 to 4.2 V, where on the bench they sit behind a 3.3 V regulator. The firmware comment says "off the cell", so this is intended, and the ADM3260 inside the isolator takes 3.0–5.5 V. But the EZO circuits hang on the isolator's output. Measure ISO1_3V9 with a full cell; it reads 3.9 V at 3.3 V in, and it must stay under the EZO's 5 V maximum.

2. The transceiver has no decoupling capacitor near it. The nearest cap on 3V3REG is C8, 25 mm away. Most MAX3485 breakout modules carry their own 100 nF — check yours. At 9600 baud on a biased bus this will work either way, but it is worth a 100 nF across the module's own VCC/GND pins in batch two.

3. The terminal blocks' bodies. The footprint is TerminalBlock_4Ucon_P3.50mm_Vertical. The pads fit any 3.5 mm terminal; the drawn body is that one part. Lay your actual terminals on the 1:1 print. The tightest spot on the board is X1 against C1, with 0.4 mm between their outlines — a deeper terminal than 8.5 mm collides there.

Mounting holes

Four M3, and not a rectangle — placement forced them apart. Measured from the board's top-left corner:

from leftfrom top
H35.05.0
H495.05.0
H15.082.0
H295.092.0

What the board says

The Value fields sit on F.Fab, which is documentation and is never printed, so before this the finished board would have carried nothing but reference designators. gen/silk.py adds 62 texts: every terminal pin by name, the cell's polarity, a plus on each electrolytic, ISOLATED on the land bridge between the two slots, PGND NEVER TO GND beside the electrodes, and a legend block in the free area right of the WaziSense connectors giving the connector functions, which control pin does what, and which parts are deliberately not fitted.

Fabrication files

out/fab/ holds everything a board house needs, and out/fab/KijaniCarrier-JLCPCB.zip is the upload: nine layers plus the two Excellon drill files, PTH and NPTH.

There used to be an out/KijaniCarrier-gerber.zip here, and it was a trap. Built on 7 October, before D1 became Q5 and before the 4-pole terminals were split, it had eleven files like the current archive and every copper layer different. It has been removed. gen/fab.sh empties out/fab/ on every run, so the file it writes is always the current one, and gen/zipcheck.py reads it back. The drill map is deliberately left out of the zip — it is a human-readable drawing with a frame around it, not a fabrication layer, and it only confuses layer detection.

FileWhat it is
KijaniCarrier-bom-jlcpcb.csv + -cpl-jlcpcb.csv53 parts, 21 lines — SMT and through-hole
KijaniCarrier-bom-smt.csv + -cpl-smt.csv25 parts — reflow only, if you order the SMT set alone
KijaniCarrier-cpl.csvthe full placement, every part, for reference

The LCSC Part # and Lib columns are filled in, and both were read off JLCPCB's own stock listing on 12 October 2026 rather than guessed — a guessed part number puts the wrong component on every board. Lib says Basic or Extended, which is what decides the feeder fee. All 21 lines resolve: 11 Basic, 10 Extended, and every one of the ten is marked Economic and Standard, so no single part forces the dearer assembly tier.

The terminals are KF350-3.5 (C474892 2-pole, C474893 3-pole), not KF128. KF128 does not exist at this pitch at all; searching only for that name once produced a false "3.50 mm is not stocked", which nearly bought a complete 5.08 mm redesign of this board.

The socket rows

The board has one footprint per module, U4 to U7, but each takes two socket strips 17.78 mm apart, and an assembly line places one part per designator. The assembly files therefore name the rows U4A … U7B, with positions computed from the real pad coordinates, and those names are printed on the silkscreen beside each row so nobody has to read the CPL alone to find them.

U1 (boost) and U3 (RS485) are the same idea with one row each: the footprint is drawn as the whole plug-in module, but the assembler fits only the pin socket. Its placement point is therefore the centre of the pads, not of the footprint outline.

An early Rev C placement file had this wrong for U1. Its socket sat at Y −67.00 in the CPL instead of −72.50 — 5.5 mm above its holes — because centroid.py used the courtyard of the whole Pololu module. The assembler's placement preview showed it. gen/cplcheck.py now runs in fab.sh and fails the build if any through-hole point is more than 0.05 mm off the centre of its own holes, or any SMT point lies outside its pads. To check another set of fabrication files, pass its folder: python gen/cplcheck.py <folder>.

When JLCPCB sends the placement render, check position as well as rotation: every socket and terminal must cover its holes, and no plated hole may be left showing white.

Splitting the footprints in the board itself would have been the other way round. It was rejected on purpose: it would have moved about sixty net references, and a net swapped in that edit would have passed every check, because it would still be connected — just connected wrongly.

Only three positions stay empty

R11 and R12 (the isolators carry their own pull-ups) and U2 (JP1 does its job). Everything else is fitted; you plug in the six modules.

Rebuilding

gen/build.sh regenerates the board from scratch and ends with fillboard.py, which fills the pours with KiCad's own filler and saves in place. Without that last step the file on disk has empty pours, every GND pad reads as unconnected and the stitching vias read as dangling, none of which is true of the real board.

The generators overwrite KijaniCarrier.kicad_pcb outright, so anything edited in pcbnew is lost on the next build. They now refuse to be imported for the same reason — importing nets.py once silently appended a second set of ten zones to a board that had just been saved from KiCad.

The coordinate convention

The board sits at an offset of 20 mm from the sheet origin, and KiCad's Y axis grows downward while the placement plan's grows upward. So a part the plan puts at (64, 24) appears in KiCad at (84, 96).

planKiCad
xx + 20
y120 − y

What to check first

  1. The four custom footprints against the real modules. Print KijaniSpace.pretty at 1:1 — kicad-cli fp export svg produces one file per footprint — and lay the parts on the paper. Pin 1 is the square pad.
  2. The slot. It runs across both chains at y 82.6–85.0 in KiCad coordinates, between each isolator and its EZO circuit.
  3. The pH run. U6 pin 5 (PRB) to J7: 8.1 mm, and it stays below the slot. That distance is the one constraint in this layout that is not negotiable.

Parts

75
QtyValueFootprintReferences
6100kResistor_SMD:R_0603_1608MetricR1, R3, R4, R6, R13, R17
4100nFCapacitor_SMD:C_0603_1608MetricC3, C5, C10, C11
41uFCapacitor_SMD:C_0603_1608MetricC6, C7, C13, C16
4AO3401APackage_TO_SOT_SMD:SOT-23Q1, Q3, Q5, Q6
4M3MountingHole:MountingHole_3.2mm_M3H1, H2, H3, H4
3M2.5 WaziSenseMountingHole:MountingHole_2.7mm_M2.5H5, H6, H7
210kResistor_SMD:R_0603_1608MetricR2, R5
210uFCapacitor_SMD:C_0805_2012MetricC9, C12
210uF 50VCapacitor_SMD:C_1206_3216MetricC14, C15
22N7002Package_TO_SOT_SMD:SOT-23Q2, Q4
2470RResistor_SMD:R_0603_1608MetricR7, R8
2470uFCapacitor_THT:CP_Radial_D10.0mm_P5.00mmC4, C8
247k DNPResistor_SMD:R_0603_1608MetricR11, R12
2I2C_PORTKijaniSpace:TerminalBlock_KF350_1x02J5A, J5B
2PROBE1KijaniSpace:TerminalBlock_KF350_1x02X3A, X3B
2PROBE2KijaniSpace:TerminalBlock_KF350_1x02X4A, X4B
10R DNPResistor_SMD:R_0603_1608MetricJP1
11.5nFCapacitor_SMD:C_0603_1608MetricC17
1100uFCapacitor_THT:CP_Radial_D5.0mm_P2.00mmC1
1120RResistor_SMD:R_0603_1608MetricRT1
1200kResistor_SMD:R_0603_1608MetricR15
122kResistor_SMD:R_0603_1608MetricR16
13.3uHInductor_SMD:L_Sunlord_SWPA4030SL1
147kResistor_SMD:R_0603_1608MetricR14
14k7Resistor_SMD:R_0603_1608MetricR9
1ANALOGTerminalBlock_4Ucon:TerminalBlock_4Ucon_1x03_P3.50mm_VerticalJ3
1BAT_INTerminalBlock_4Ucon:TerminalBlock_4Ucon_1x02_P3.50mm_VerticalJ6
1BE-IVI ECKijaniSpace:BE_IVI_IsolatorU5
1BE-IVI pHKijaniSpace:BE_IVI_IsolatorU4
1BOOST SELJumper:SolderJumper-3_P1.3mm_Bridged12_RoundedPad1.0x1.5mm_NumberLabelsJP3
1CELLTerminalBlock_4Ucon:TerminalBlock_4Ucon_1x02_P3.50mm_VerticalX1
1DIGITALTerminalBlock_4Ucon:TerminalBlock_4Ucon_1x03_P3.50mm_VerticalJ4
1DS18B20TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x03_P3.50mm_VerticalX2
1EZO-ECKijaniSpace:EZO_CircuitU7
1EZO-pHKijaniSpace:EZO_CircuitU6
1MCP1700-3302Package_TO_SOT_SMD:SOT-23U2
1PTC 2.6AResistor_SMD:R_1812_4532MetricF1
1SM712Package_TO_SOT_SMD:SOT-23D3
1SMA ECConnector_Coaxial:SMA_Amphenol_132291_VerticalJ9
1SMA pHConnector_Coaxial:SMA_Amphenol_132291_VerticalJ7
1SMBJ5.0ADiode_SMD:D_SMBD2
1SS14Diode_SMD:D_SMAD4
1SW_1TerminalBlock_4Ucon:TerminalBlock_4Ucon_1x02_P3.50mm_VerticalJ2
1THVD1406Package_SO:SOIC-8_3.9x4.9mm_P1.27mmU3
1TPS61085Package_SO:MSOP-8_3x3mm_P0.65mmU8
1U3V9F12KijaniSpace:Pololu_U3V9F12U1

More from Waziup/WaziDev

1
  • 2 layers100 × 100 mmKijaniCarrier, the printed circuit board in 3D

    KijaniCarrier

    Waziup/WaziDev

    WaziDev official repository

    56 parts5Apache-2.0

Similar boards

  • 2 layers79.2 × 68.8 mmEnvironment slave, the printed circuit board in 3D

    Environment slave

    pcbcupid/Lora-Wireless-Sensor-Network

    A wireless sensor network based on Lora and esp32C3. This repo will help you build a complete product from scratch to finish!

    72 parts30MIT
  • 4 layers84 × 55 mmGrblPANEL, the printed circuit board in 3D

    GrblPANEL

    dresco/grblpanel_main_board

    KiCad implementation of a grblPANEL control board

    143 parts2
  • 2 layers49 × 47 mmGNI plutoclocker, the printed circuit board in 3D

    GNI plutoclocker

    cr1tbit/GNI-plutoclocker

    This device serves a couple of roles: allowing clocking adalm-pluto by an external clock source of broad frequency range injecting clock signal into RF trace, for clocking modded LNB supplying LNB with DC voltage…

    72 parts6MIT
  • 2 layers79.5 × 50 mmLine usb master, the printed circuit board in 3D

    Line usb master

    c4deszes/bike-line-protocol

    Serial messaging protocol

    111 parts1
  • 4 layers82 × 34 mmFiber phantom, the printed circuit board in 3D

    Fiber phantom

    jayis1/hacker-devices

    Full-cycle hardware designs for security research devices — each device is a subfolder with its own README, schematics, PCB layout, and software stack

    17 parts2
  • 2 layers118.3 × 68 mmField eyes v2.before spreading, the printed circuit board in 3D

    Field eyes v2.before spreading

    louismomo66/field-eyes_pcb

    Open fieldeyesv2.kicadpro in KiCad 9. The whole circuit is one flat schematic (fieldeyesv2.kicadsch): all 168 parts are on a single page, in four framed sections.

    168 parts
  • 4 layers220 × 160 mmBroadcast rtu carrier rev a, the printed circuit board in 3D

    Broadcast rtu carrier rev a

    Gecesars/01-plataforma-telemetria-broadcast

    Plataforma Unificada de Telemetria de Estação Transmissora — baseline técnica e acompanhamento do projeto

    94 parts
  • 2 layers54 × 42 mmSib, the printed circuit board in 3D

    Sib

    RangeMaster370/kicad-sensor-interface-board

    SIB-1 sensor-interface board in KiCad

    36 parts

Questions

How big is the KijaniCarrier PCB?

It measures 100 × 100 mm and has 2 copper layers, 1.6 mm thick.

What parts does the KijaniCarrier use?

75 parts, built around the THVD1406D, DS18B20, MCP1700-3302E/TT and TPS61085. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the KijaniCarrier KiCad files?

From the Waziup/WaziDev repository on GitHub, at the commit this page was made from: the board is examples/LoRaWAN/Water_monitoring/WaterSensorNode/pcb/revD/KijaniCarrier.kicad_pcb. PCBKit opens the board in the browser, with nothing to install.

Can I use the KijaniCarrier design in my own project?

Its repository publishes it under the Apache-2.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).