diff --git a/PCB/EDU-INTEL/bom_scripts/bom2csv.xsl b/PCB/EDU-INTEL/bom_scripts/bom2csv.xsl
new file mode 100644
index 00000000..1991d495
--- /dev/null
+++ b/PCB/EDU-INTEL/bom_scripts/bom2csv.xsl
@@ -0,0 +1,93 @@
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+
+
+ Reference, Value, Footprint, Datasheet
+
+
+
+ ,
+
+
+ &nl;
+
+
+
+
+
+
+
+ ,
+ ,
+ ,
+
+
+ &nl;
+
+
+
+
+
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PCB/EDU-INTEL/bom_scripts/bom_csv_grouped_by_value.py b/PCB/EDU-INTEL/bom_scripts/bom_csv_grouped_by_value.py
new file mode 100644
index 00000000..4d3ddb3b
--- /dev/null
+++ b/PCB/EDU-INTEL/bom_scripts/bom_csv_grouped_by_value.py
@@ -0,0 +1,134 @@
+#
+# Example python script to generate a BOM from a KiCad generic netlist
+#
+# Example: Sorted and Grouped CSV BOM
+#
+
+from __future__ import print_function
+
+# Import the KiCad python helper module and the csv formatter
+import kicad_netlist_reader
+import csv
+import sys
+
+
+if len(sys.argv) != 3:
+ print("Usage ", __file__, "", file=sys.stderr)
+ sys.exit(1)
+
+
+# Generate an instance of a generic netlist, and load the netlist tree from
+# the command line option. If the file doesn't exist, execution will stop
+net = kicad_netlist_reader.netlist(sys.argv[1])
+
+# Open a file to write to, if the file cannot be opened output to stdout
+# instead
+try:
+ f = open(sys.argv[2], 'w')
+except IOError:
+ e = "Can't open output file for writing: " + sys.argv[2]
+ print( __file__, ":", e, sys.stderr )
+ f = sys.stdout
+
+# subset the components to those wanted in the BOM, controlled
+# by block in kicad_netlist_reader.py
+components = net.getInterestingComponents()
+
+compfields = net.gatherComponentFieldUnion(components)
+partfields = net.gatherLibPartFieldUnion()
+
+# remove Reference, Value, Datasheet, and Footprint, they will come from 'columns' below
+partfields -= set( ['Reference', 'Value', 'Datasheet', 'Footprint'] )
+
+columnset = compfields | partfields # union
+
+# prepend an initial 'hard coded' list and put the enchillada into list 'columns'
+columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
+
+# Create a new csv writer object to use as the output formatter
+out = csv.writer( f, lineterminator='\n', delimiter=',', quotechar='\"', quoting=csv.QUOTE_MINIMAL )
+
+# override csv.writer's writerow() to support utf8 encoding:
+def writerow( acsvwriter, columns ):
+ utf8row = []
+ for col in columns:
+ utf8row.append( str(col).encode('utf8') )
+ acsvwriter.writerow( utf8row )
+
+# Output a set of rows as a header providing general information
+writerow( out, ['Source:', net.getSource()] )
+writerow( out, ['Date:', net.getDate()] )
+writerow( out, ['Tool:', net.getTool()] )
+writerow( out, ['Component Count:', len(components)] )
+writerow( out, [] )
+writerow( out, ['Individual Components:'] )
+writerow( out, [] ) # blank line
+writerow( out, columns )
+
+# Output all the interesting components individually first:
+row = []
+for c in components:
+ del row[:]
+ row.append('') # item is blank in individual table
+ row.append('') # Qty is always 1, why print it
+ row.append( c.getRef() ) # Reference
+ row.append( c.getValue() ) # Value
+ row.append( c.getLibName() + ":" + c.getPartName() ) # LibPart
+ #row.append( c.getDescription() )
+ row.append( c.getFootprint() )
+ row.append( c.getDatasheet() )
+
+ # from column 7 upwards, use the fieldnames to grab the data
+ for field in columns[7:]:
+ row.append( c.getField( field ) );
+
+ writerow( out, row )
+
+
+writerow( out, [] ) # blank line
+writerow( out, [] ) # blank line
+writerow( out, [] ) # blank line
+
+writerow( out, ['Collated Components:'] )
+writerow( out, [] ) # blank line
+writerow( out, columns ) # reuse same columns
+
+
+
+# Get all of the components in groups of matching parts + values
+# (see kicad_netlist_reader.py)
+grouped = net.groupComponents(components)
+
+
+# Output component information organized by group, aka as collated:
+item = 0
+for group in grouped:
+ del row[:]
+ refs = ""
+
+ # Add the reference of every component in the group and keep a reference
+ # to the component so that the other data can be filled in once per group
+ for component in group:
+ if len(refs) > 0:
+ refs += ", "
+ refs += component.getRef()
+ c = component
+
+ # Fill in the component groups common data
+ # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
+ item += 1
+ row.append( item )
+ row.append( len(group) )
+ row.append( refs );
+ row.append( c.getValue() )
+ row.append( c.getLibName() + ":" + c.getPartName() )
+ row.append( net.getGroupFootprint(group) )
+ row.append( net.getGroupDatasheet(group) )
+
+ # from column 7 upwards, use the fieldnames to grab the data
+ for field in columns[7:]:
+ row.append( net.getGroupField(group, field) );
+
+ writerow( out, row )
+
+f.close()
diff --git a/PCB/EDU-INTEL/bom_scripts/bom_cvs.xsl b/PCB/EDU-INTEL/bom_scripts/bom_cvs.xsl
new file mode 100644
index 00000000..78a61086
--- /dev/null
+++ b/PCB/EDU-INTEL/bom_scripts/bom_cvs.xsl
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+ ,"
+ ","
+ ","
+
+
+ "&nl;
+
+
+
+
+ Qty,partname,description,lib&nl;
+
+
+
+
+
diff --git a/PCB/EDU-INTEL/bom_scripts/kicad_netlist_reader.py b/PCB/EDU-INTEL/bom_scripts/kicad_netlist_reader.py
new file mode 100644
index 00000000..c60e4432
--- /dev/null
+++ b/PCB/EDU-INTEL/bom_scripts/kicad_netlist_reader.py
@@ -0,0 +1,736 @@
+#
+# KiCad python module for interpreting generic netlists which can be used
+# to generate Bills of materials, etc.
+#
+# No string formatting is used on purpose as the only string formatting that
+# is current compatible with python 2.4+ to 3.0+ is the '%' method, and that
+# is due to be deprecated in 3.0+ soon
+#
+
+"""
+ @package
+ Generate a HTML BOM list.
+ Components are sorted and grouped by value
+ Fields are (if exist)
+ Ref, Quantity, Value, Part, Datasheet, Description, Vendor
+"""
+
+
+from __future__ import print_function
+import sys
+import xml.sax as sax
+import re
+import pdb
+
+#---------------------------------------------------------------------
+
+# excluded_fields is a list of regular expressions. If any one matches a field
+# from either a component or a libpart, then that will not be included as a
+# column in the BOM. Otherwise all columns from all used libparts and components
+# will be unionized and will appear. Some fields are impossible to blacklist, such
+# as Ref, Value, Footprint, and Datasheet. Additionally Qty and Item are supplied
+# unconditionally as columns, and may not be removed.
+excluded_fields = [
+ #'Price@1000'
+ ]
+
+
+# You may exlude components from the BOM by either:
+#
+# 1) adding a custom field named "Installed" to your components and filling it
+# with a value of "NU" (Normally Uninstalled).
+# See netlist.getInterestingComponents(), or
+#
+# 2) blacklisting it in any of the three following lists:
+
+
+# regular expressions which match component 'Reference' fields of components that
+# are to be excluded from the BOM.
+excluded_references = [
+ 'TP[0-9]+' # all test points
+ ]
+
+
+# regular expressions which match component 'Value' fields of components that
+# are to be excluded from the BOM.
+excluded_values = [
+ 'MOUNTHOLE',
+ 'SCOPETEST',
+ 'MOUNT_HOLE',
+ 'SOLDER_BRIDGE.*'
+ ]
+
+
+# regular expressions which match component 'Footprint' fields of components that
+# are to be excluded from the BOM.
+excluded_footprints = [
+ #'MOUNTHOLE'
+ ]
+
+#--------------------------------------------------------------------
+
+
+class xmlElement():
+ """xml element which can represent all nodes of the netlist tree. It can be
+ used to easily generate various output formats by propogating format
+ requests to children recursively.
+ """
+ def __init__(self, name, parent=None):
+ self.name = name
+ self.attributes = {}
+ self.parent = parent
+ self.chars = ""
+ self.children = []
+
+ def __str__(self):
+ """String representation of this netlist element
+
+ """
+ return self.name + "[" + self.chars + "]" + " attr_count:" + str(len(self.attributes))
+
+ def formatXML(self, nestLevel=0, amChild=False):
+ """Return this element formatted as XML
+
+ Keywords:
+ nestLevel -- increases by one for each level of nesting.
+ amChild -- If set to True, the start of document is not returned.
+
+ """
+ s = ""
+
+ indent = ""
+ for i in range(nestLevel):
+ indent += " "
+
+ if not amChild:
+ s = "\n"
+
+ s += indent + "<" + self.name
+ for a in self.attributes:
+ s += " " + a + "=\"" + self.attributes[a] + "\""
+
+ if (len(self.chars) == 0) and (len(self.children) == 0):
+ s += "/>"
+ else:
+ s += ">" + self.chars
+
+ for c in self.children:
+ s += "\n"
+ s += c.formatXML(nestLevel+1, True)
+
+ if (len(self.children) > 0):
+ s += "\n" + indent
+
+ if (len(self.children) > 0) or (len(self.chars) > 0):
+ s += "" + self.name + ">"
+
+ return s
+
+ def formatHTML(self, amChild=False):
+ """Return this element formatted as HTML
+
+ Keywords:
+ amChild -- If set to True, the start of document is not returned
+
+ """
+ s = ""
+
+ if not amChild:
+ s = """
+
+
+
+
+
+
+
+ """
+
+ s += "
" + self.name + " " + self.chars + "
"
+ for a in self.attributes:
+ s += "
" + a + " = " + self.attributes[a] + "
"
+
+ s += "
\n"
+
+ for c in self.children:
+ s += c.formatHTML(True)
+
+ if not amChild:
+ s += """