--- jupyter: description: 'An EasyEDA board in planex2d: px.FromKiCad converts an EasyEDA Standard or Professional file with KiCad 10''s kicad-cli, fills its pours, and returns its layers as shapes, the copper named by net. The example is the control board of OpenSTM, an open-source scanning tunneling microscope.' --- # 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](index.md) for how the tool is found and how the copper pours of a converted board are filled. ```python import collections import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row ``` ## 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](OpenSTM_ControlBoard.zip) and its [license](OpenSTM_ControlBoard_LICENSE.txt). ```python 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²") ``` 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): ```python 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)]) ``` ```python nbsphinx="hide-input" copper = {"face": "#c87533", "ink": "#6b3410", "dashed": [outline], "lw": 0.4} Row((fcu, "F.Cu, the top layer", copper), (bcu, "B.Cu, the bottom layer", copper), caption="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). ```python nbsphinx="hide-input" top = {"face": "#2f6b45", "ink": "#1d4029", "lw": 0.4, "fills": [(board["F.Mask"], "#d9b46c"), (board["F.SilkS"], "#f4f4ee")]} Row((outline, "F.Mask and F.SilkS over the outline", top), caption="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: ```python 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") ``` The pins lie on the grid to rounding.