board
RITHESH LED Display
Public Unreviewedby Rithesh03
A 300 x 70 mm PCB that spells RITHESH with 107 WS2812B RGB LEDs, controlled over Wi-Fi.
main
Rithesh03
Update 3: PCB design completed - routed board, manufacturing package (prototype, do not order yet), WLED guide, final checklist, Hydrogen feedback
1ed072d
13d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
"""Create the project footprint library hardware/rithesh_fp.pretty.
- ESP32-C3-MINI-1: Espressif's official KiCad footprint (espressif/kicad-libraries,
CC-BY-SA 4.0 with the KiCad design exception), copied unchanged except for a note in
the description. It was checked pad by pad against the datasheet land pattern
(Figure 11-1) by tools/check_footprints.py.
- LED_XL-5050RGBC-2812B: drawn from the XINGLIGHT datasheet's "Recommended Soldering
Pattern" (page 11): four 1.3 x 1.3 mm pads, 3.1 mm apart across and 1.8 mm apart down.
Usage: python3 make_footprints.py <path to Espressif ESP32-C3-MINI-1.kicad_mod>
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
HW = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LIB = os.path.join(HW, "rithesh_fp.pretty")
# ---- LED: manufacturer recommended pattern ----------------------------------------
PAD = 1.3 # pad size (square), mm
PX = 3.1 / 2 + PAD / 2 # 2.2 : pad centre, across
PY = 1.8 / 2 + PAD / 2 # 1.55 : pad centre, down
BODY = 2.5 # half of the 5.0 mm body
SILK_W = 0.15 # JLCPCB minimum silkscreen line width
SILK = 2.62 # silkscreen body outline (clear of pads by >= 0.2 mm)
CY_X = PX + PAD / 2 + 0.25 # courtyard: pads + 0.25 mm
CY_Y = BODY + 0.3
def line(x1, y1, x2, y2, layer, w):
return (f' (fp_line (start {x1:.3f} {y1:.3f}) (end {x2:.3f} {y2:.3f}) '
f'(stroke (width {w}) (type solid)) (layer "{layer}"))\n')
def led_footprint():
pads = {"1": (-PX, -PY), "2": (-PX, PY), "3": (PX, PY), "4": (PX, -PY)}
s = ('(footprint "LED_XL-5050RGBC-2812B" (version 20221018) (generator rithesh_gen)\n'
' (layer "F.Cu")\n'
' (descr "XINGLIGHT XL-5050RGBC-2812B addressable RGB LED, 5.0x5.0mm. Manufacturer '
'recommended soldering pattern (datasheet p.11): 4 pads 1.3x1.3mm, 3.1mm apart across, '
'1.8mm apart down (centres +/-2.2 x +/-1.55mm). Pin 1 VDD, 2 DO, 3 GND, 4 DI.")\n'
' (tags "LED RGB addressable WS2812 5050 XL-5050RGBC-2812B")\n'
' (attr smd)\n'
' (fp_text reference "REF**" (at 0 -3.6) (layer "F.SilkS")\n'
' (effects (font (size 1 1) (thickness 0.15))))\n'
' (fp_text value "XL-5050RGBC-2812B" (at 0 3.7) (layer "F.Fab")\n'
' (effects (font (size 1 1) (thickness 0.15))))\n'
' (fp_text user "${REFERENCE}" (at 0 0) (layer "F.Fab")\n'
' (effects (font (size 0.8 0.8) (thickness 0.12))))\n')
# Fab: 5.0 x 5.0 body with the corner notch at pin 3 (GND), as in the datasheet drawing
b = BODY
for (x1, y1, x2, y2) in [(-b, -b, b, -b), (b, -b, b, b - 1), (b, b - 1, b - 1, b),
(b - 1, b, -b, b), (-b, b, -b, -b)]:
s += line(x1, y1, x2, y2, "F.Fab", 0.1)
s += ' (fp_circle (center 0 0) (end 1.9 0) (stroke (width 0.1) (type solid)) (fill none) (layer "F.Fab"))\n'
# Silkscreen: top and bottom edges, short side segments between the pads, pin-1 dot
s += line(-SILK, -SILK, SILK, -SILK, "F.SilkS", SILK_W)
s += line(-SILK, SILK, SILK, SILK, "F.SilkS", SILK_W)
side = PY - PAD / 2 - 0.3 # 0.6 mm: stays 0.3 mm from pads
s += line(-SILK, -side, -SILK, side, "F.SilkS", SILK_W)
s += line(SILK, -side, SILK, side, "F.SilkS", SILK_W)
s += (' (fp_circle (center -3.5 -2.6) (end -3.3 -2.6) (stroke (width 0.15) (type solid)) '
'(fill solid) (layer "F.SilkS"))\n')
# Courtyard
for (x1, y1, x2, y2) in [(-CY_X, -CY_Y, CY_X, -CY_Y), (CY_X, -CY_Y, CY_X, CY_Y),
(CY_X, CY_Y, -CY_X, CY_Y), (-CY_X, CY_Y, -CY_X, -CY_Y)]:
s += line(x1, y1, x2, y2, "F.CrtYd", 0.05)
for num, (x, y) in pads.items():
s += (f' (pad "{num}" smd rect (at {x:.3f} {y:.3f}) (size {PAD} {PAD}) '
'(layers "F.Cu" "F.Paste" "F.Mask"))\n')
return s + ")\n"
def trim_front_silk(name, limit, keep):
"""Clip silkscreen lines so they stay on the kept side of y = limit (footprint mm).
Edits the .kicad_mod file directly (KiCad 7's Python API cannot save footprints)."""
import sexp
path = os.path.join(LIB, name + ".kicad_mod")
fp = sexp.parse(open(path).read())
ok = (lambda y: y >= limit) if keep == "greater" else (lambda y: y <= limit)
removed = clipped = 0
for item in list(fp):
if not (isinstance(item, list) and item and item[0] == "fp_line"):
continue
layer = sexp.find1(item, "layer")
if not layer or layer[1] != "F.SilkS":
continue
a, b = sexp.find1(item, "start"), sexp.find1(item, "end")
ay, by = float(a[2]), float(b[2])
if not ok(ay) and not ok(by):
fp.remove(item)
removed += 1
continue
for p, o in ((a, b), (b, a)):
py, oy = float(p[2]), float(o[2])
if not ok(py):
t = (limit - oy) / (py - oy)
p[1] = float(o[1]) + (float(p[1]) - float(o[1])) * t
p[2] = limit
clipped += 1
open(path, "w").write(sexp.dump(fp) + "\n")
print(f"{name}: front silkscreen trimmed ({removed} removed, {clipped} clipped)")
def main():
os.makedirs(LIB, exist_ok=True)
with open(os.path.join(LIB, "LED_XL-5050RGBC-2812B.kicad_mod"), "w") as f:
f.write(led_footprint())
src = open(sys.argv[1]).read()
marker = '(descr "ESP32-C3-MINI-1: '
assert marker in src
src = src.replace(marker, '(descr "Official Espressif footprint (espressif/kicad-libraries, CC-BY-SA 4.0 '
'with KiCad design exception), checked against datasheet Figure 11-1. ESP32-C3-MINI-1: ', 1)
# JLCPCB needs silkscreen lines >= 0.15 mm; the official file uses 0.12 mm.
n = src.count('(stroke (width 0.12) (type solid)) (layer "F.SilkS")')
src = src.replace('(stroke (width 0.12) (type solid)) (layer "F.SilkS")',
'(stroke (width 0.15) (type solid)) (layer "F.SilkS")')
print(f"ESP32: widened {n} silkscreen lines from 0.12 to 0.15 mm")
with open(os.path.join(LIB, "ESP32-C3-MINI-1.kicad_mod"), "w") as f:
f.write(src)
# M3 mounting hole: KiCad's standard footprint (3.2 mm non-plated hole, no copper ring),
# copied so the project library is self-contained.
mh = open("/usr/share/kicad/footprints/MountingHole.pretty/MountingHole_3.2mm_M3.kicad_mod").read()
with open(os.path.join(LIB, "MountingHole_3.2mm_M3.kicad_mod"), "w") as f:
f.write(mh)
# USB-C receptacle: KiCad's standard HRO TYPE-C-31-M-12 footprint, copied into the project.
usb = open("/usr/share/kicad/footprints/Connector_USB.pretty/"
"USB_C_Receptacle_HRO_TYPE-C-31-M-12.kicad_mod").read()
with open(os.path.join(LIB, "USB_C_Receptacle_HRO_TYPE-C-31-M-12.kicad_mod"), "w") as f:
f.write(usb)
# Both parts sit with their front exactly on the board edge, so silkscreen that reaches
# that edge would be clipped by JLCPCB: trim it in the library copies.
trim_front_silk("ESP32-C3-MINI-1", -8.0, keep="greater") # antenna end is -y
trim_front_silk("USB_C_Receptacle_HRO_TYPE-C-31-M-12", 3.35, keep="less") # opening is +y
with open(os.path.join(HW, "fp-lib-table"), "w") as f:
f.write('(fp_lib_table\n (version 7)\n (lib (name "rithesh_fp")(type "KiCad")'
'(uri "${KIPRJMOD}/rithesh_fp.pretty")(options "")(descr "RITHESH project footprints"))\n)\n')
print("wrote", LIB)
if __name__ == "__main__":
main()