I'm considering making a PCB to use as a heater for a 3d printer bed. This has been common practice for ~10 years (https://www.thingiverse.com/thing:3919).
I was playing around with KiCad and python, and made a PCB trace that could (in theory) be infinitely long.
Steps:
- open KiCad, open the symbol/footpad tool, create a new library, create a new component with two pad and 3-4 copper lines linking them, save
- open the library file in notepad++, and find the copper traces and pads
- replace them with the output of the following code, then save.
#create Hilbert Sequence
hilbert_seq = "a"
scale = 100
num_recurr = 5
for _ in range(num_recurr):
new_seq = ""
for char in hilbert_seq:
if char == "a":
new_seq += "-bF+aFa+Fb-"
elif char == "b":
new_seq += "+aF-bFb-Fa+"
else:
new_seq += char
hilbert_seq = new_seq
#print(hilbert_seq)
#create sequence of points
fwd_step = scale / (2**num_recurr-1)
lines = []
pos_x, pos_y = 0.0, 0.0
angle = 0
for char in hilbert_seq:
if char == "F":
if angle == 0: dx, dy = fwd_step, 0
if angle == 1: dx, dy = 0, fwd_step
if angle == 2: dx, dy = -fwd_step, 0
if angle == 3: dx, dy = 0, -fwd_step
new_x, new_y = pos_x+dx, pos_y+dy
lines.append( ((pos_x, pos_y), (new_x, new_y)) )
#print((angle, pos_x, pos_y, dx, dy, new_x, new_y))
pos_x, pos_y = new_x, new_y
elif char == "+":
angle = (angle+1) % 4
elif char == "-":
angle = (angle-1) % 4
#trace_width, fwd_step - space between traces for square, needs to be re-computed for circle
trace_width = 1 #fwd_step - 0.3
lines = [(l[0],l[1],trace_width) for l in lines]
#optionally, map into a circle with a very equiareal transform
import math
sqr2 = math.sqrt(2)
def to_circle(l):
#https://marc-b-reynolds.github.io/math/2017/01/08/SquareDisc.html
x1,y1,x2,y2,width = l[0][0],l[0][1],l[1][0],l[1][1],l[2]
x1_s,y1_s,x2_s,y2_s = 2*x1/scale-1,2*y1/scale+1, 2*x2/scale-1,2*y2/scale+1
#print((x1_s,y1_s,x2_s,y2_s))
def square_to_disc(x,y):
if x*x>y*y: return (math.copysign(1,x)*math.sqrt(2*x*x-y*y)/sqr2, y/sqr2)
else: return (x/sqr2, math.copysign(1,y)*math.sqrt(2*y*y-x*x)/sqr2)
x1_plus_s, y1_plus_s = square_to_disc(x1_s, y1_s)
x2_plus_s, y2_plus_s = square_to_disc(x2_s, y2_s)
return ( (x1_plus_s*scale, y1_plus_s*scale), (x2_plus_s*scale, y2_plus_s*scale), width)
lines = [to_circle(l) for l in lines]
#output
def line_to_text(l):
return "(fp_line (start {start_x} {start_y}) (end {end_x} {end_y}) (layer \"F.Cu\") (width {width}) (tstamp 1947ea8e-3ea5-493b-ab1c-4e8c5a675398))".format(start_x=l[0][0], start_y=l[0][1], end_x=l[1][0], end_y=l[1][1], width=l[2])
def pad_to_text(pos, padnum):
return "(pad \"{padnum}\" thru_hole circle (at {pad_x} {pad_y}) (size 1.524 1.524) (drill 0.762) (layers *.Cu *.Mask) (tstamp 69ab893d-e72a-4903-8a42-16f6b5eb229b))".format(padnum=padnum, pad_x=pos[0], pad_y=pos[1])
for l in lines: print(line_to_text(l))
print(pad_to_text(lines[0][0],1))
print(pad_to_text(lines[len(lines)-1][1],2))
(Edit: This circuit is very typically connected to a 3D printer circuit, so a thermistor reads a temperature to a microprocessor, and the microprocessor turns the heating on/off, transformed 12V switched with a mosfet for the cheaper RAMPS 1.4 board, electrical mains through a solid state relay for high end systems. The objective is to reach a stated temperature with uniform heating (lookup 'heatbed temperature per filament'). Heat itself goes through several layers, PCB, then Kapton heat transfer tape, then a printing bed made of glass or PEI or some other material. PCB/heatbed geometry is dictated by the 3d printer's geometry. High power output is seen as convenient as it means the printer can startup more quickly.)
My question is:
- what are the constraints (electric field between traces, inter-trace, heat) ?
- does this have any practical application ? It raises the resistance of the trace.