ESP32 Continued
Contents
ESP32 Continued#
Now let’s break up the large python script into subcircuits so it’s easier to manage. We’ll make functions that create and connect all of our parts similar to breakout_ic()
we learned previously.
Create Subcircuits#
Start by moving the ESP32
to a function, along with some capacitors. In the function arguments we set the default names for power rails
to V3_3
and GND
:
def esp32_circuit(V3_3=Net('V3_3'), GND=Net('GND')):
esp32 = Part('RF_Module','ESP32-WROOM-32', footprint='RF_Module:ESP32-WROOM-32')
c_bulk = Part('Device', 'C', value='10uF', footprint='Capacitor_SMD:C_0603_1608Metric')
c_bypass = Part('Device', 'C', value='100nF', footprint='Capacitor_SMD:C_0603_1608Metric')
V3_3 += esp32[1], c_bulk[1], c_bypass[1]
GND += esp32[2], c_bulk[2], c_bypass[2]
return esp32
The reset circuit
can be put into a function as well. This circuit only needs the MCU_EN
signal, in addition to the assumed V3_3
and GND
power rails:
def reset_circuit(MCU_EN, V3_3=Net('V3_3'), GND=Net('GND')):
r_en_pullup = Part('Device', 'R', value='10k', footprint='Resistor_SMD:R_0603_1608Metric')
r_en_pullup[1] += MCU_EN
r_en_pullup[2] += V3_3
c_en_filter = Part('Device', 'C', value='10uF', footprint='Capacitor_SMD:C_0603_1608Metric')
c_en_filter[1] += MCU_EN
c_en_filter[2] += GND
switch = Part('Switch','SW_DPST', footprint='Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH1xxxxxxV2')
switch[1,2] += MCU_EN
switch[3,4] += GND
Part Templates#
In the examples so far we’ve used many resistors
and capacitors
that share the same symbol
and footprint
. We can create a part template
that can be copied when we need by defining a part and adding dest=TEMPLATE
in it’s declaration:
r = Part('Device', 'R', footprint='Resistor_SMD:R_0603_1608Metric', dest=TEMPLATE)
c = Part('Device', 'C', footprint='Capacitor_SMD:C_0603_1608Metric', dest=TEMPLATE)
Use the new functions in the circuits above to reduce the size and complexity of the circuit:
def esp32_circuit(V3_3=Net('V3_3'), GND=Net('GND')):
esp32 = Part('RF_Module','ESP32-WROOM-32', footprint='RF_Module:ESP32-WROOM-32')
esp32['VDD'] += V3_3
esp32['GND'] += GND
c1 = c(value='10uF')
c1[1] += V3_3
c1[2] += GND
c2 = c(value='100nF')
c2[1] += V3_3
c2[2] += GND
return esp32
def reset_circuit(MCU_EN, V3_3=Net('V3_3'), GND=Net('GND')):
switch = Part('Switch','SW_DPST', footprint='Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH1xxxxxxV2')
switch[1,2] += MCU_EN
switch[3,4] += GND
c1 = c(value='10uF')
c1[1] += MCU_EN
c1[2] += GND
r1 = r(value='10k')
r1[1] += MCU_EN
r1[2] += V3_3
Additional Circuits#
Program circuit#
Now we’ll add a new ciruit which will allow us to program the ESP32:
def esp32_prog(GPIO0, RST, DTR, EN):
npn1 = Part('Transistor_FET', 'DMN2041L', footprint='Package_TO_SOT_SMD:SOT-23')
npn1['S'] += DTR
npn1['D'] += GPIO0
r1 = r(value='10k')
r1[1] += npn1['G']
r1[2] += RST
npn2 = Part('Transistor_FET', 'DMN2041L', footprint='Package_TO_SOT_SMD:SOT-23')
npn2['S'] += EN
npn2['D'] += RST
r2 = r(value='10k')
r2[1] += npn2['G']
r2[2] += DTR
USB Circuit#
We need to be able to talk to the ESP32 over serial for programming and communication. The USB circuit will also be our source of power for the ESP32 board. Add a USB circuit
to our board:
def usb_interface(RX, TX, EN, IO0, V3_3=Net('V3_3'), GND=Net('GND')):
cp2104 = Part('Interface_USB','CP2104', footprint='Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm')
# Connect power pins
cp2104['VDD','VIO','REGIN'] += V3_3
cp2104['GND'] += GND
# IC capacitors
c1 = c(value='10uF')
c1[1] += V3_3
c1[2] += GND
c2 = c(value='100nF')
c2[1] += V3_3
c2[2] += GND
# Cap to allow ROM writing
c3 = c(value='4.7uF')
c3[1] += cp2104['VPP']
c3[2] += GND
# Make mini USB_B connector
usb_conn = Part('Connector', 'USB_A', footprint='Connector_USB:USB_A_Molex_67643_Horizontal')
# Connect IC to connecotr
usb_conn['VBUS'] += cp2104['VBUS']
usb_conn['D-'] += cp2104['D-']
usb_conn['D+'] += cp2104['D+']
# usb_conn['ID'] # not connected
usb_conn['GND','Shield'] += GND
# uart resistors
r1 = r(value='470R')
r1[1] += cp2104['RXD']
r1[2] += TX
r2 = r(value='470R')
r2[1] += cp2104['TXD']
r2[2] += RX
return usb_conn, cp2104
Voltage regulator#
This circuit will convert the 5v USB supply into the 3.3V
to the ESP32.
def linear_regulator(VIN, V3_3=Net('V3_3'), GND=Net('GND')):
reg = Part('Regulator_Linear', 'NCP1117-3.3_SOT223', footprint='Package_TO_SOT_SMD:SOT-223-3_TabPin2')
reg['GND'] += GND
reg['VI'] += VIN
reg['VO'] += V3_3
# Bulk caps
c1 = c(value='10uF')
c1[1] += VIN
c1[2] += GND
c2 = c(value='10uF')
c2[1] += V3_3
c2[2] += GND
Final script#
See Code
from skidl import *
r = Part('Device', 'R', footprint='Resistor_SMD:R_0603_1608Metric', dest=TEMPLATE)
c = Part('Device', 'C', footprint='Capacitor_SMD:C_0603_1608Metric', dest=TEMPLATE)
def esp32_circuit(V3_3=Net('V3_3'), GND=Net('GND')):
esp32 = Part('RF_Module','ESP32-WROOM-32', footprint='RF_Module:ESP32-WROOM-32')
esp32['VDD'] += V3_3
esp32['GND'] += GND
c1 = c(value='10uF')
c1[1] += V3_3
c1[2] += GND
c2 = c(value='100nF')
c2[1] += V3_3
c2[2] += GND
return esp32
def reset_circuit(MCU_EN, V3_3=Net('V3_3'), GND=Net('GND')):
switch = Part('Switch','SW_DPST', footprint='Button_Switch_THT:SW_Push_2P1T_Toggle_CK_PVA1xxH1xxxxxxV2')
switch[1,2] += MCU_EN
switch[3,4] += GND
c1 = c(value='10uF')
c1[1] += MCU_EN
c1[2] += GND
r1 = r(value='10k')
r1[1] += MCU_EN
r1[2] += V3_3
def usb_interface(RX, TX, EN, IO0, V3_3=Net('V3_3'), GND=Net('GND')):
cp2104 = Part('Interface_USB','CP2104', footprint='Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm')
# Connect power pins
cp2104['VDD','VIO','REGIN'] += V3_3
cp2104['GND'] += GND
# IC capacitors
c1 = c(value='10uF')
c1[1] += V3_3
c1[2] += GND
c2 = c(value='100nF')
c2[1] += V3_3
c2[2] += GND
# Cap to allow ROM writing
c3 = c(value='4.7uF')
c3[1] += cp2104['VPP']
c3[2] += GND
# Make mini USB_B connector
usb_conn = Part('Connector', 'USB_A', footprint='Connector_USB:USB_A_Molex_67643_Horizontal')
# Connect IC to connecotr
usb_conn['VBUS'] += cp2104['VBUS']
usb_conn['D-'] += cp2104['D-']
usb_conn['D+'] += cp2104['D+']
# usb_conn['ID'] # not connected
usb_conn['GND','Shield'] += GND
# uart resistors
r1 = r(value='470R')
r1[1] += cp2104['RXD']
r1[2] += TX
r2 = r(value='470R')
r2[1] += cp2104['TXD']
r2[2] += RX
return usb_conn, cp2104
def esp32_prog(GPIO0, RST, DTR, EN):
npn1 = Part('Transistor_FET', 'DMN2041L', footprint='Package_TO_SOT_SMD:SOT-23')
npn1['S'] += DTR
npn1['D'] += GPIO0
r1 = r(value='10k')
r1[1] += npn1['G']
r1[2] += RST
npn2 = Part('Transistor_FET', 'DMN2041L', footprint='Package_TO_SOT_SMD:SOT-23')
npn2['S'] += EN
npn2['D'] += RST
r2 = r(value='10k')
r2[1] += npn2['G']
r2[2] += DTR
def linear_regulator(VIN, V3_3=Net('V3_3'), GND=Net('GND')):
reg = Part('Regulator_Linear', 'NCP1117-3.3_SOT223', footprint='Package_TO_SOT_SMD:SOT-223-3_TabPin2')
reg['GND'] += GND
reg['VI'] += VIN
reg['VO'] += V3_3
# Bulk caps
c1 = c(value='10uF')
c1[1] += VIN
c1[2] += GND
c2 = c(value='10uF')
c2[1] += V3_3
c2[2] += GND
def breakout_ic(ic, header):
headers=[header()]
counter = 1
# Iterate through IC pins in pin order and connect them to a header
for n in range(1, len(ic.pins) +1): # ic.pins may not be in footprint order
pin = ic[n]
headers[-1][counter] += pin
counter += 1
# Check if we've reached the end of the header
if counter > len(header.pins):
counter = 1
# Add another copy of the header to the list
headers.append(header.copy())
# Disconnect all the pins of the new header
for i in headers[-1].pins:
i.disconnect()
esp32 = esp32_circuit()
reset_circuit(esp32['EN'])
usb_conn, usb_ic = usb_interface(esp32['RXD0/IO3'], esp32['TXD0/IO1'], esp32['EN'], esp32['IO0'])
esp32_prog(esp32['IO0'], esp32['EN'], usb_ic["~{RTS}"], usb_ic["~{DTR}"])
linear_regulator(usb_conn['VBUS'])
header = Part('Connector', 'Conn_01x05_Male',
footprint='Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical', dest=TEMPLATE)
breakout_ic(esp32, header)
generate_pcb()
We can now import the netlist
into KiCAD as we’ve done before and continue on with the board design process.
Summary#
In this chapter we completed the ESP32 dev board using Python functions to wrap individual circuits.