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')]
F.Cu, the top layer
(a) F.Cu, the top layer
B.Cu, the bottom layer
(b) B.Cu, the bottom layer
Figure 1: The copper layers of an EasyEDA board, read through KiCad

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).

F.Mask and F.SilkS over the outline
Figure 2: The top of the OpenSTM control board

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.