// Create BOM // Arne Rossius 2020 // LCSC part numbers (for CSV export) string pn_vals[] = { ">R0805","510", "680", "1k", "22k", "100k", "120k", "150k" ,">C0805","100n", "4.7u", ">SOT23","MMBT3904","" }; string pn_lcsc[] = { "", "C17734","C17798","C17513","C17560","C17407","C17436","C17470","", "C49678","C1779","", "C20526", "" }; string fn = ""; string name, prefix, suffix, type = "xxx"; string value[]; string designator[]; string footprint[]; int qty[]; int i, n = 0; numeric string list[]; int index = 0, sortc = 4; // Save BOM as text file void save_text(void) { fn = dlgFileSave("Save BOM as Text", fn, "*.txt"); if (fn == "") return; if (strstr(strlwr(fn), ".txt", -4) < 0) fn += ".txt"; int length[] = { 3, 5, 9, 11 }; string str; for (i = 0; i < n; i++) { sprintf(str, "%d", qty[i]); if (strlen(str) > length[0]) length[0] = strlen(str); if (strlen(value[i]) > length[1]) length[1] = strlen(value[i]); if (strlen(footprint[i]) > length[2]) length[2] = strlen(footprint[i]); if (strlen(designator[i]) > length[3]) length[3] = strlen(designator[i]); } output(fn, "wt") { printf("%s - BOM\n\n", name); sprintf(str, " Qty%%%ds| Value%%%ds| Footprint%%%ds| Designators\n", length[0] - 2, length[1] - 4, length[2] - 8); printf(str, "", "", ""); str = "-----------"; for (i = 0; i < length[0] + length[1] + length[2] + length[3]; i++) { str += "-"; } printf(str + "\n"); for (i = 0; i < n; i++) { string val = value[i]; int c, idx = 0, len; len = length[1] - strlen(val); sprintf(str, "%%s%%%ds", len); sprintf(val, str, val, ""); // pad value (sprintf padding can't handle 2-byte chars) while ((c = strchr(val, 0xb5, idx)) >= 0) { val = strsub(val, 0, c) + "\xC2\xB5" + strsub(val, c+1); // convert 'mu' to UTF-8 idx = c + 2; } sprintf(str, " %%%dd | %%-%ds | %%-%ds | %%s\n", length[0], length[1], length[2]); printf(str, qty[i], val, footprint[i], designator[i]); } int t = time(); printf("\nExported %04d-%02d-%02d %02d:%02d:%02d\n", t2year(t), t2month(t)+1, t2day(t), t2hour(t), t2minute(t), t2second(t)); } dlgMessageBox(";Saved BOM to " + fn); } // Save BOM as HTML void save_html(void) { fn = dlgFileSave("Save BOM as HTML", fn, "*.htm"); if (fn == "") return; if (strstr(strlwr(fn), ".htm", -4) < 0) fn += ".htm"; output(fn, "wt") { printf("\n\n" "\n" "%s\n\n\n

%s - BOM

\n" "\n\n", name, name); for (i = 0; i < n; i++) { printf("\n", qty[i], value[i], footprint[i], designator[i]); } int t = time(); printf("
QtyValueFootprintDesignators
%d%s%s%s
\n

Exported %04d-%02d-%02d %02d:%02d:%02d\n" "\n\n", t2year(t), t2month(t)+1, t2day(t), t2hour(t), t2minute(t), t2second(t)); } dlgMessageBox(";Saved BOM to " + fn); } // Save BOM as CSV (JLCPCB compatible) void save_csv(void) { fn = dlgFileSave("Save BOM as CSV", fn, "*.csv"); if (fn == "") return; if (strstr(strlwr(fn), ".csv", -4) < 0) fn += ".csv"; output(fn, "wt") { printf("Qty,Comment,Designator,Footprint,LCSC\n"); for (i = 0; i < n; i++) { string val = value[i]; int c; while ((c = strchr(val, 0xb5)) >= 0) val[c] = 'u'; // replace 'mu' characters string fp = ""; string lcsc = ""; for (int j = 0; 1; j++) { if (pn_vals[j] == "") break; if (pn_vals[j][0] == '>') { fp = strsub(pn_vals[j], 1); continue; } if ((pn_vals[j] == val) && (fp == footprint[i])) { lcsc = pn_lcsc[j]; break; } } printf("%d,\"%s\",\"%s\",\"%s\",\"%s\"\n", qty[i], val, designator[i], footprint[i], lcsc); } } dlgMessageBox(";Saved BOM to " + fn); } if (!schematic) { dlgMessageBox(":This ULP must be run in a schematic!"); exit(1); } schematic(S) { S.parts(P) { if (!P.device || !P.device.package) continue; name = P.name; for (i = strlen(name) - 1; isdigit(name[i]); i--); prefix = strsub(name, 0, i+1); suffix = strsub(name, i+1); int found = 0; for (i = 0; i < n; i++) { if ((value[i] == P.value) && (footprint[i] == P.device.package.name) && (strstr(designator[i], prefix) == 0) ) { qty[i]++; designator[i] += ", " + name; found = 1; break; } /* } else { if (dlgMessageBox("No match:\nFootprint: " + P.device.package.name + " / " + footprint[i] + "\nValue: " + P.value + " / " + value[i] + "\nDesignator: " + prefix + " / " + designator[i], "OK", "Abort") == 1) exit(0); break; } */ } if (!found) { value[n] = P.value; designator[n] = P.name; footprint[n] = P.device.package.name; qty[n] = 1; n++; } } name = S.name; int slash = strrchr(name, '/'); if (slash >= 0) name = strsub(name, slash+1); if (strrstr(name, ".sch") == strlen(name) - 4) name = strsub(name, 0, strlen(name) - 4); } for (i = 0; i < n; i++) { sprintf(list[i], "%d\t%s\t%s\t%s", qty[i], value[i], footprint[i], designator[i]); } int ok = dlgDialog("Bill of Materials") { dlgListView("Qty\tValue\tFootprint\tDesignators", list, index, sortc); dlgHBoxLayout { dlgPushButton("Save &Text") save_text(); dlgPushButton("Save &HTML") save_html(); dlgPushButton("Save JLCPCB &CSV") save_csv(); dlgPushButton("-Exit") dlgReject(); } };