Created
March 5, 2021 07:52
-
-
Save dzindra/dde9ede4fea0c1488ab9435aa2ec3d2e to your computer and use it in GitHub Desktop.
JLCPCB BOM and CPL generator for Eagle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#usage "<b>Data generation for JLCPCB Assembly service</b>\n" | |
"<p>" | |
"Generates files for smds on the top and bottom layers " | |
"wich can be used with mounting machines. " | |
"The x and y coordinates (units: mm) of the SMD elements are calculated " | |
"as mean of maximum and mimimum value of the smds origin points. " | |
"The calculated value does not necessarily fit with the origin " | |
"point of the part in the layout." | |
"All SMD elements populated in currently set assembly variant are considered." | |
"</p>" | |
"<p>" | |
"Additional part attributes that are checked" | |
"</p><ul>" | |
"<li><b>JLC_ROT</b> - number of degreed to add to existing part rotation. Positive or negative integer.</li>" | |
"<li><b>JLC_VALUE</b> - override default part value. String.</li>" | |
"<li><b>JLC_PACKAGE</b> - override default part package. String.</li>" | |
"<li><b>JLC_MOUNT</b> - <i>NO</i> - do not mount part even if it is SMD. <i>FORCE</i> - mount part event if it is not and SMD part (useful for THT parts).</li>" | |
"</ul>" | |
"<p>" | |
"<author>Author: support@cadsoft.de</author><br>" | |
"<author>Author: D Roosendaal</author><br>" | |
"<author>Author: jindra@thecave.cz</author><br>" | |
// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED | |
// 2019-09-20: Modified by D. Roosendaal to output a JLCPCB compatible CSV and BOM! | |
// UNVERIFIED as of yet. Use at your own risk. | |
// 2021-03-05: Modified by J. Dolezy to support additional attributes that affect the output files. | |
// Additional part attributes that are checked | |
// JLC_ROT - number of degreed to add to existing part rotation. Positive or negative integer. | |
// JLC_VALUE - override default part value. String. | |
// JLC_PACKAGE - override default part package. String. | |
// JLC_MOUNT - "NO" - do not mount part even if it is SMD. "FORCE" - mount part event if it is not and SMD part (useful for THT parts). | |
// | |
// *** YOU CAN POPULATE THIS LIST TO CORRECT ROTATION, AND TO [option] ADD SMT ORDER CODE | |
// | |
// Value, package, additional rotation (pos or neg), optional JLC SMT order code to populate the BOM | |
string Modify[] = { "BC847B;SOT23-BEC;-90", | |
"BC857B;SOT23-BEC;-90", | |
"12.288MHz;ABM3B;-90", | |
"BAV99;SOT-23;90", | |
"1117-3.3;SOT223;-90", | |
"BS170;SOT-23;90", | |
"BSS84;SOT23;-90", | |
"8V2;SOT23;-90", | |
"S9012;SOT23-BEC;-90", | |
"47uF;CT3216;180", | |
"100uF;CT3528;180" }; | |
//, | |
//"SS14;DO214AC;-180" }; | |
// At this moment, JLCPCB seems to have a bug which stops properly reading of parts if one or more components have the optional order code. | |
// This may or may not be useful to you in the future.... your decision. | |
//"ADAU1701;LQFP-48;;C26394"}; | |
if (board) board(B) { | |
int NumParts=0; | |
int i, j; | |
string PartName[], PartValue[], PartPackage[]; | |
int PartSorted[]; | |
string List; | |
string Code; | |
// Get filename | |
string fileName = dlgFileSave("Save File", filesetext(B.name, "CPL.csv"), "*.csv"); | |
if (fileName == "") exit(0); | |
output(fileName) { | |
printf("Designator,Mid X,Mid Y,Layer,Rotation\n"); | |
B.elements(E) if (E.populate) { | |
int wasSmd, | |
xmax =-2147483648, | |
xmin = 2147483647, | |
ymax = xmax, | |
ymin = xmin; | |
wasSmd = 0; | |
E.package.contacts(C) { | |
if ((C.smd && C.smd.layer == 1) || (E.attribute["JLC_MOUNT"]=="FORCE")) { | |
wasSmd = 1; | |
if (C.x > xmax) xmax = C.x; | |
if (C.y > ymax) ymax = C.y; | |
if (C.x < xmin) xmin = C.x; | |
if (C.y < ymin) ymin = C.y; | |
// If MOUNT attribute is NO, don't mount it... | |
if ((E.attribute["JLC_MOUNT"]=="NO")) wasSmd=0; | |
} | |
} | |
if (wasSmd) { | |
i=360; | |
// Look for the part&package in the Modify list, and apply rotation if applicable | |
if (E.package.name==lookup(Modify, E.value, 1, ';')) { | |
i+=strtol(lookup(Modify, E.value, 2, ';')); | |
} else if (E.attribute["JLC_ROT"]) { | |
i+=strtol(E.attribute["JLC_ROT"]); | |
} | |
i+=E.angle; | |
i%=360; | |
//Designator,Mid X,Mid Y,Layer,Rotation | |
printf("%s,%5.3fmm,%5.3fmm,Top,%d\n",E.name, u2mm((xmin + xmax)/2), u2mm((ymin + ymax)/2),i); | |
// Get part details to create the BOM, only populate parts that have a value. | |
if(E.value) { | |
PartSorted[NumParts]=0; | |
PartName[NumParts]=E.name; | |
PartValue[NumParts]= E.attribute["JLC_VALUE"] == "" ? E.value : E.attribute["JLC_VALUE"]; | |
PartPackage[NumParts++]= E.attribute["JLC_PACKAGE"] == "" ? E.package.name : E.attribute["JLC_PACKAGE"]; | |
} | |
} | |
} | |
B.elements(E) if (E.populate) { | |
int wasSmd, | |
xmax =-2147483648, | |
xmin = 2147483647, | |
ymax = xmax, | |
ymin = xmin; | |
wasSmd = 0; | |
E.package.contacts(C) { | |
if ((C.smd && C.smd.layer == 16) || (E.attribute["JLC_MOUNT"]=="FORCE")) { | |
wasSmd = 1; | |
if (C.x > xmax) xmax = C.x; | |
if (C.y > ymax) ymax = C.y; | |
if (C.x < xmin) xmin = C.x; | |
if (C.y < ymin) ymin = C.y; | |
// If MOUNT attribute is NO, don't mount it... | |
if ((E.attribute["JLC_MOUNT"]=="NO")) wasSmd=0; | |
} | |
} | |
if (wasSmd) { | |
i=360; | |
// Look for the part&package in the Modify list, and apply rotation if applicable | |
if (E.package.name==lookup(Modify, E.value, 1, ';')) { | |
i+=strtol(lookup(Modify, E.value, 2, ';')); | |
} else if (E.attribute["JLC_ROT"]) { | |
i+=strtol(E.attribute["JLC_ROT"]); | |
} | |
i+=E.angle; | |
i%=360; | |
//Designator,Mid X,Mid Y,Layer,Rotation | |
printf("%s,%5.3fmm,%5.3fmm,Bottom,%d\n",E.name, u2mm((xmin + xmax)/2), u2mm((ymin + ymax)/2),i); | |
// Get part details to create the BOM, only populate parts that have a value. | |
if(E.value) { | |
PartSorted[NumParts]=0; | |
PartName[NumParts]=E.name; | |
PartValue[NumParts]= E.attribute["JLC_VALUE"] == "" ? E.value : E.attribute["JLC_VALUE"]; | |
PartPackage[NumParts++]= E.attribute["JLC_PACKAGE"] == "" ? E.package.name : E.attribute["JLC_PACKAGE"]; | |
} | |
} | |
} | |
} | |
// Get filename for the BOM | |
string fileNameBOM = dlgFileSave("Save File", filesetext(B.name, "BOM.csv"), "*.csv"); | |
if (fileNameBOM == "") exit(0); | |
output(fileNameBOM) { | |
// Comment,Designator,Footprint - Comment being the value | |
List+="Comment,Designator,Footprint\n"; | |
for(i=0; i<NumParts;i++) { | |
if(!PartSorted[i]) { | |
List+="\""+PartValue[i]+"\",\""+PartName[i]; | |
for(j=i+1; j<NumParts;j++) { | |
// Find other parts with the same value AND package | |
if(!PartSorted[j] && PartValue[i]==PartValue[j] && PartPackage[i]==PartPackage[j]) { | |
// Found! | |
PartSorted[j]=1; | |
List+=","+PartName[j]; | |
} | |
} | |
// Look for the part&package in the Modify list, and append order Code, if it exists | |
if (PartPackage[i]==lookup(Modify, PartValue[i], 1, ';')) { | |
if(lookup(Modify, PartValue[i], 3, ';')) | |
Code=","+lookup(Modify, PartValue[i], 3, ';'); | |
} | |
else | |
Code=""; | |
List+="\",\""+PartPackage[i]+Code+"\"\n"; | |
} | |
} | |
printf ("%s", List); | |
} | |
} | |
else { | |
dlgMessageBox("\n Start this ULP in a Board \n"); | |
exit (0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment