|
1 | | -# psychogenic-eeschema |
2 | | -eeschema schematic manipulation |
| 1 | +# kicad skip: S-expression kicad file python parser |
| 2 | + |
| 3 | +Copyright © 2024 Pat Deegan, [psychogenic.com](https://psychogenic.com/) |
| 4 | + |
| 5 | + |
| 6 | +This library is focused on scripted manipulations of schematics (and other) kicad _source_ _files_ |
| 7 | +and allows any item to be edited in a hopefully intuitive way. |
| 8 | + |
| 9 | +It also provides helpers for common operations. |
| 10 | + |
| 11 | + |
| 12 | +Kicad schematic, layout and other files are stored as trees of s-expressions, like |
| 13 | + |
| 14 | +``` |
| 15 | +(kicad_sch (version 20230121) (generator eeschema) |
| 16 | + (uuid 20adca1d-43a1-4784-9682-8b7dd1c7d330) |
| 17 | + (title_block (title "My Demo Board") (date "2024-01-31") (rev "1.0.5") |
| 18 | + (company "Psychogenic Technologies") (comment 1 "(C) 2023, 2024 Pat Deegan") |
| 19 | + ) |
| 20 | + (lib_symbols |
| 21 | + (symbol "74xx:74CBTLV3257" (in_bom yes) (on_board yes) |
| 22 | + (property "Reference" "U" (at 17.78 1.27 0) (effects (font (size 1.27 1.27)))) |
| 23 | + (property "Value" "74CBTLV3257" (at 15.24 -1.27 0)(effects (font (size 1.27 1.27)))) |
| 24 | + ... |
| 25 | + (symbol (lib_id "SomeLib:Partname") (at 317.5 45.72 0) (unit 1) |
| 26 | + (in_bom yes) (on_board yes) (dnp no) |
| 27 | + (uuid 342c76f3-b2b8-40b2-a0b0-d83e480188cc) |
| 28 | + (property "Reference" "J4" (at 311.15 29.21 0)(effects (font (size 1.27 1.27)))) |
| 29 | + (property "Value" "TT04_BREAKOUT_REVB" (at 317.5 76.2 0) (effects (font (size 1.27 1.27)))) |
| 30 | + (property "Footprint" "TinyTapeout:TT04_BREAKOUT_SMB" (at 320.04 81.28 0) |
| 31 | + (effects (font (size 1.27 1.27)) hide)) |
| 32 | + (pin "A1" (uuid 5132b98d-ec39-4a2b-974d-c4d323ea43eb)) |
| 33 | + (pin "A10" (uuid bf1f9b27-0b93-4778-ac69-684e16bea09c)) |
| 34 | + (pin "A19" (uuid 43e3e4f6-008a-4ddf-a427-4414db85dcbb)) |
| 35 | + ... |
| 36 | +``` |
| 37 | +which are great for machine parsing, but not so much for a quick scripted manipulation. |
| 38 | + |
| 39 | +With skip, you can quickly explore and modify the contents of a schematic (or, really, any |
| 40 | +kicad s-expression file). |
| 41 | + |
| 42 | + |
| 43 | +## Examples |
| 44 | + |
| 45 | +Effort has been made to make exploring the contents easy. This means: |
| 46 | + |
| 47 | +#### named attributes |
| 48 | +Where possible, collections have named attributes: **do use** TAB-completion, schem.<TAB><TAB> etc |
| 49 | + |
| 50 | +Examples |
| 51 | + |
| 52 | +``` |
| 53 | +>>> schem.symbol.U TAB TAB |
| 54 | +``` |
| 55 | +outputs (something like, depending on schematic): |
| 56 | + |
| 57 | +``` |
| 58 | + schem.symbol.U1 schem.symbol.U3 schem.symbol.U4_B schem.symbol.U4_D |
| 59 | + schem.symbol.U2 schem.symbol.U4_A schem.symbol.U4_C schem.symbol.U4_E |
| 60 | +``` |
| 61 | + |
| 62 | +and |
| 63 | + |
| 64 | +``` |
| 65 | +>>> schem.symbol.U1.property. TAB TAB |
| 66 | +``` |
| 67 | + |
| 68 | +outputs |
| 69 | + |
| 70 | +``` |
| 71 | + schem.symbol.U1.property.Characteristics |
| 72 | + schem.symbol.U1.property.Datasheet |
| 73 | + schem.symbol.U1.property.DigikeyPN |
| 74 | + schem.symbol.U1.property.Footprint |
| 75 | + schem.symbol.U1.property.MPN |
| 76 | + schem.symbol.U1.property.Reference |
| 77 | + schem.symbol.U1.property.Value |
| 78 | +``` |
| 79 | + |
| 80 | +#### representation |
| 81 | +`__repr__` and `__str__` overrides so you have an idea what you're looking at. Just enter the variable in the REPL console and it should spit something sane out. |
| 82 | + |
| 83 | + |
| 84 | +``` |
| 85 | +>>> schem |
| 86 | +<Schematic 'samp/my_schematic.kicad_sch'> |
| 87 | +
|
| 88 | +>>> schem.symbol[23] |
| 89 | +<symbol C28> |
| 90 | +
|
| 91 | +>>> schem.symbol[23].property |
| 92 | +<Collection [<PropertyString Reference = 'C28'>, <PropertyString Value = '100nF'>, |
| 93 | + <PropertyString Footprint = 'Capacitor_SMD:C_0402_1005Metric'>, |
| 94 | + <PropertyString Datasheet = '~'>, <PropertyString JLC = ''>, |
| 95 | + <PropertyString Part_number = ''>, <PropertyString MPN = 'CL05A104KA5NNNC'>, |
| 96 | + <PropertyString Characteristics = '100n 10% 25V XR 0402'>, |
| 97 | + <PropertyString MPN_ALT = 'GRM155R71E104KE14J'>]> |
| 98 | +
|
| 99 | +>>> schem.wire[22] |
| 100 | +<Wire [314.96, 152.4] - [317.5, 152.4]> |
| 101 | +
|
| 102 | +>>> schem.text[4] |
| 103 | +<text 'Options fo...'> |
| 104 | +
|
| 105 | +# and much more, e.g. for library symbols, junctions, labels, whatever's in there |
| 106 | +``` |
| 107 | + |
| 108 | + |
| 109 | +## Quick walkthrough |
| 110 | + |
| 111 | +Here's some sample interaction with the library. The basic functions allow you to view and edit attributes. More involved helpers let you search and crawl the schematic to find things. |
| 112 | + |
| 113 | + |
| 114 | +#### basic |
| 115 | + |
| 116 | +``` |
| 117 | +# load a schematic |
| 118 | +schem = skip.Schematic('samp/my_schem.kicad_sch') |
| 119 | +
|
| 120 | +# loop over all components, treat collection as array |
| 121 | +for component in schem.symbol: |
| 122 | + component # do something |
| 123 | +
|
| 124 | +# search through the symbols by reference or value, using regex or starts_with |
| 125 | +>>> schem.symbol.reference_matches(r'(C|R)2[158]') |
| 126 | +[<symbol C25>, <symbol C28>, <symbol R25>, <symbol C21>, <symbol R21>, <symbol R28>] |
| 127 | +
|
| 128 | +>>> sorted(schem.symbol.value_startswith('10k')) |
| 129 | +[<symbol R12>, <symbol R30>, <symbol R31>, <symbol R32>, <symbol R33>, <symbol R42>, |
| 130 | + <symbol R43>, <symbol R44>, <symbol R45>, <symbol R46>, <symbol R47>, <symbol R48>, |
| 131 | + <symbol R8>, <symbol R9>] |
| 132 | +
|
| 133 | +# or refer to components by name |
| 134 | +conn = schem.symbol.J15 |
| 135 | +
|
| 136 | +# symbols have attributes |
| 137 | +if not conn.in_bom: |
| 138 | + conn.dnp = True |
| 139 | +
|
| 140 | +# and properties (things that can be named by user) |
| 141 | +for p in conn.property: |
| 142 | + print(f'{p.name} = {p.value}') |
| 143 | +# will output "Reference = J15", "MPN = USB4500-03-0-A" etc |
| 144 | +
|
| 145 | +# and change properties, of course |
| 146 | +>>> conn.property.MPN.value = 'ABC123' |
| 147 | +>>> conn.property.MPN |
| 148 | +<PropertyString MPN = 'ABC123'> |
| 149 | +
|
| 150 | +
|
| 151 | +
|
| 152 | +# clone pretty much anything and modify it |
| 153 | +>>> mpn_alt = conn.property.MPN.clone() |
| 154 | +>>> mpn_alt.name = 'MPN_ALT' |
| 155 | +>>> mpn_alt.value = 'ABC456' |
| 156 | +
|
| 157 | +
|
| 158 | +# save the result |
| 159 | +>>> schem.write('/tmp/newfile.kicad_sch') |
| 160 | +
|
| 161 | +# let's verify the change |
| 162 | +>>> schem.read('/tmp/newfile.kicad_sch') |
| 163 | +>>> conn = schem.symbol.J15 |
| 164 | +>>> for p in conn.property: |
| 165 | + p |
| 166 | +<PropertyString Reference = 'J15'> |
| 167 | +<PropertyString Value = 'USB4500-03-0-A_REVA'> |
| 168 | +<PropertyString MPN = 'ABC123'> |
| 169 | +<PropertyString MPN_ALT = 'ABC456'> |
| 170 | +
|
| 171 | +``` |
| 172 | + |
| 173 | + |
| 174 | +### Helpers |
| 175 | + |
| 176 | +Collections, and the source file object, have helpers to locate relevant elements. |
| 177 | + |
| 178 | +#### Attached elements |
| 179 | + |
| 180 | +Where applicable, such as for symbols (components), directly attached elements (via wires) |
| 181 | +may be listed using attached_* |
| 182 | + |
| 183 | +``` |
| 184 | +>>> conn = sch.symbol.J15 |
| 185 | +
|
| 186 | +>>> conn.attached_ TAB TAB |
| 187 | + conn.attached_all |
| 188 | + conn.attached_global_labels |
| 189 | + conn.attached_labels |
| 190 | + conn.attached_symbols |
| 191 | + conn.attached_wires |
| 192 | +>>> conn.attached_symbols |
| 193 | + [<symbol C3>, <symbol R16>, <symbol C47>, |
| 194 | + <symbol R20>, <symbol C46>, <symbol R21>, |
| 195 | + <symbol F1>] |
| 196 | +
|
| 197 | +>>> conn.attached_labels |
| 198 | + [<label CC2>, <label CC1>] |
| 199 | + |
| 200 | +# or list everything attached |
| 201 | +>>> conn.attached_all |
| 202 | + [<symbol C3>, <symbol R16>, <symbol C47>, |
| 203 | + <symbol R20>, <symbol C46>, <symbol R21>, |
| 204 | + <symbol F1>, <global_label usb_d->, |
| 205 | + <global_label usb_d+>, <label CC2>, |
| 206 | + <label CC1>] |
| 207 | +
|
| 208 | +``` |
| 209 | + |
| 210 | +#### finding elements |
| 211 | + |
| 212 | +Symbols may be located by reference or value |
| 213 | + |
| 214 | + * schem.symbol.reference_matches(REGEX) |
| 215 | + |
| 216 | + * schem.symbol.reference_startswith(STR) |
| 217 | + |
| 218 | +In addition, containers with 'positionable' elements have |
| 219 | + |
| 220 | + * within_circle(X, Y, RADIUS) |
| 221 | + |
| 222 | + * within_rectangle(X1, Y1, X2, Y2) |
| 223 | + |
| 224 | + * within_reach_of(ELEMENT, RADIUS) # circle around ELEMENT's position |
| 225 | + |
| 226 | + * between_elements(ELEMENT1, ELEMENT2) # within rectangle formed by two elements |
| 227 | + |
| 228 | +A collection will only return results of it's own type (e.g. global_labels.within_circle() will only return global labels). |
| 229 | + |
| 230 | +To search the entire schematic, the same within* and between() methods exist on the source file object (the schem, here). |
| 231 | +This will return any label, global label or symbol with the constrained bounds. |
| 232 | + |
| 233 | +``` |
| 234 | +
|
| 235 | +>>> schem.global_label.between_elements(sch.symbol.C49, sch.symbol.R16) |
| 236 | + [<global_label usb_d->, <global_label usb_d+>, |
| 237 | + <global_label usb_d+>, <global_label usb_d->] |
| 238 | +>>> |
| 239 | +>>> schem.between_elements(sch.symbol.C49, sch.symbol.R16) |
| 240 | + [<symbol C44>, <symbol #PWR0121>, <symbol J15>, |
| 241 | + <symbol C47>, <symbol #PWR0122>, <symbol D5>, <symbol C48>, |
| 242 | + <symbol C45>, <symbol #PWR021>, <symbol C49>, <symbol R20>, |
| 243 | + <symbol C46>, <symbol #FLG02>, <symbol F1>, <symbol C3>, |
| 244 | + <symbol R16>, <symbol R21>, <symbol D6>, <label CC2>, |
| 245 | + <label CC1>, <label vfused>, <global_label usb_d->, |
| 246 | + <global_label usb_d+>, <global_label usb_d+>, |
| 247 | + <global_label usb_d->] |
| 248 | +
|
| 249 | +``` |
| 250 | + |
| 251 | +## API |
| 252 | + |
| 253 | +Further documentation to come. For now, use the above, load a schematic in a console, and explore what's available using TAB-/code-completion. |
| 254 | + |
| 255 | +The main thing to note is that the leaves of the tree usually have a ".value" that should be used to access the actual contents and, especially, for setting values. Have fun. |
| 256 | + |
| 257 | +2024-04-04 |
| 258 | +Pat Deegan |
| 259 | + |
| 260 | + |
0 commit comments