EasyEDA
EasyEDA, an EDA tool that runs in the browser and on the desktop, comes in two editions. EasyEDA Standard exports a board as a .json file or a .zip archive, and a project of EasyEDA Professional is read as an .epro file or a .zip archive. px.FromKiCad converts both with kicad-cli, the command line tool of KiCad 10 or later, and returns the same dict of layers as for a KiCad board. See KiCad, Altium, Eagle and more for how the tool is found and how the
copper pours of a converted board are filled.
[1]:
import collections
import planex as px
The board
The example is the control board of OpenSTM, an open-source scanning tunneling microscope by Dimsmary. The file is the EasyEDA Professional project from the test data of KiCad’s EasyEDA importer, which KiCad redistributes under the MIT license. Download: OpenSTM_ControlBoard.zip and its license.
[3]:
board = px.FromKiCad("OpenSTM_ControlBoard.zip")
for name, layer in board.items():
print(f"{name:10} {len(layer.faces):4} faces, {1e6 * layer.mass:9.2f} mm²")
PlanexWarning: FromKiCad: 326 texts left out (not supported yet)
F.Cu 236 faces, 2439.48 mm²
B.Cu 60 faces, 8480.34 mm²
Edge.Cuts 1 faces, 9660.21 mm²
F.Paste 396 faces, 371.09 mm²
F.SilkS 332 faces, 356.39 mm²
B.SilkS 20 faces, 81.45 mm²
F.Mask 470 faces, 813.81 mm²
B.Mask 74 faces, 449.38 mm²
Dwgs.User 32 faces, 10.38 mm²
F.CrtYd 148 faces, 95.68 mm²
F.Fab 164 faces, 66.41 mm²
User.1 459 faces, 234.96 mm²
PlanexWarning: FromKiCad: 8 line ends on Edge.Cuts run past the outline (0.25 to 0.51 mm) and are left out
The two copper layers have their faces named by net and the drill holes cut out, and the other layers hold what KiCad prints there. The text on the board is not read yet, and FromKiCad reports how much it left out. The second warning comes from the outline, whose straight sides run a little past its rounded corners. The outline itself is closed, and FromKiCad leaves these short pieces out and says how long they are.
Copper per net
Besides its signals, the board has supply nets of +12 V and −12 V, and its analog ground AGND covers most of the bottom layer (Figure 1):
[4]:
fcu, bcu, outline = board["F.Cu"], board["B.Cu"], board["Edge.Cuts"]
for name, layer in (("F.Cu", fcu), ("B.Cu", bcu)):
area = collections.Counter()
for f in layer.faces:
area[f.name or "(no net)"] += f.mass
print(name, len(area), "nets, the largest:",
[(net, f"{100 * a / outline.mass:.1f} % of the board") for net, a in area.most_common(3)])
F.Cu 67 nets, the largest: [('AGND', '9.2 % of the board'), ('-12V', '4.0 % of the board'), ('+12V', '3.4 % of the board')]
B.Cu 31 nets, the largest: [('AGND', '86.3 % of the board'), ('3V3', '0.3 % of the board'), ('+12V', '0.1 % of the board')]

(a) F.Cu, the top layer

(b) B.Cu, the bottom layer
The solder mask openings F.Mask and the silkscreen F.SilkS laid over the outline show the board as it comes from the manufacturer (Figure 2).

A check against the connector grid
The board has a connector of 2 × 7 pins on a 0.1 inch grid, whose 14 plated holes of 1.1 mm are cut out of the outline. Their centers have to lie on that grid:
[7]:
inch = 25.4e-3
pins = []
for hole in outline.faces[0].inner:
(x0, y0), (x1, y1) = hole.bounding_box
if abs(x1 - x0 - 1.1e-3) < 1e-6:
pins.append(((x0 + x1) / 2, (y0 + y1) / 2))
first = pins[0]
off = max(abs(d / (0.1 * inch) - round(d / (0.1 * inch))) * 0.1 * inch
for p in pins for d in (p[0] - first[0], p[1] - first[1]))
print(len(pins), "pins, largest distance from the 0.1 inch grid", off, "m")
14 pins, largest distance from the 0.1 inch grid 2.594369163944066e-17 m
The pins lie on the grid to rounding.