Skip to content

ATmega328P Sensor Shield: SMD Components and CNC Milling

ATmega328P Sensor Shield: SMD Components and CNC Milling hero image
Modified:
Published:

This lesson transitions from through-hole to surface mount technology. You will design a sensor shield around the ATmega328P in a TQFP-32 package, add an I2C temperature/humidity sensor (SHT30) and an SPI ambient light sensor (VEML7700), and learn CNC milling as an alternative PCB fabrication method. #KiCad #SMD #SensorShield

What We Are Building

ATmega328P Sensor Shield

A compact double-sided sensor board with the ATmega328P in its smallest through-hole-free package (TQFP-32, 0.8mm pin pitch). The board reads temperature, humidity, and ambient light through I2C and SPI buses, sending data over UART. All passives are 0805 SMD, the smallest size comfortable for hand soldering.

Board specifications:

ParameterValue
MCUATmega328P-AU (TQFP-32, 0.8mm pitch)
Clock16 MHz crystal (3.2x2.5mm SMD)
PowerUSB Micro-B 5V input, 3.3V LDO (AMS1117-3.3) for sensors
SensorsSHT30-DIS (I2C, temp/humidity), VEML7700 (I2C, ambient light)
CommunicationUART header (TX, RX, GND), ISP header
Passives0805 SMD resistors and capacitors
Size40 x 35 mm, double-sided

Bill of Materials

RefComponentPackageQuantityNotes
U1ATmega328P-AUTQFP-3210.8mm pitch
U2AMS1117-3.3SOT-22313.3V LDO for sensors
U3SHT30-DIS-BDFN-8 (2.5x2.5mm)1Temperature/humidity
U4VEML7700OPLGA (6.8x3.5mm)1Ambient light sensor
Y116 MHz3.2x2.5mm SMD1Crystal
C1, C222 pF08052Crystal load caps
C3-C6100 nF08054Decoupling
C7, C810 µF08052Regulator caps
R110 kΩ08051Reset pull-up
R2, R34.7 kΩ08052I2C pull-ups
R4330 Ω08051Power LED
D1Green LED08051Power indicator
J1USB Micro-BSMD1Power input
J22x3 pin header2.54mm1ISP header
J31x4 pin header2.54mm1UART (VCC, TX, RX, GND)
SW1Tactile switchSMD 3x6mm1Reset

SMD Footprint Fundamentals



Before drawing the schematic, let’s understand what changes with SMD components.

Package Sizes

SMD passive components come in standardized sizes named by their imperial dimensions in hundredths of an inch:

PackageSize (mm)Hand Solderable?Use Case
12063.2 x 1.6EasyPrototypes, power resistors
08052.0 x 1.25ComfortableGeneral purpose (this lesson)
06031.6 x 0.8ModerateSpace-constrained designs
04021.0 x 0.5DifficultProfessional assembly only

We use 0805 throughout this lesson. It is small enough to save board space but large enough that a steady hand with a fine-tip soldering iron can handle it.

TQFP vs DIP

The ATmega328P-AU (TQFP-32) has the same silicon as the DIP-28 version from Lesson 1, but in a 7x7mm package instead of 37x7mm. The pin pitch drops from 2.54mm to 0.8mm. This means:

  • Thinner traces between pins (you need at least 0.2mm trace width to route between TQFP pads)
  • Solder paste and a hot air station work better than an iron for TQFP soldering
  • The pinout maps differently: TQFP-32 has 32 pins (4 extra: two additional GND and two ADC pins)

Pad and Courtyard

In KiCad, every SMD footprint has:

  • Pads: copper areas where solder connects the component to the board
  • Courtyard: a clearance boundary showing how close other components can be placed
  • Fab layer: the component outline showing its physical dimensions

When placing components, keep courtyards from overlapping. KiCad’s DRC checks this automatically.

Schematic Capture



Create a new KiCad project named atmega328p-sensor-shield.

Power Supply

This board has two voltage rails: 5V from USB for the MCU, and 3.3V from the LDO for the sensors.

  1. Place the USB Micro-B connector. Add USB_B_Micro from the Connector library. This provides 5V and GND from any USB power source.

  2. Place the 3.3V LDO. Add AMS1117-3.3 (SOT-223). Connect its input to the USB 5V through a 10 µF capacitor (C7). Connect its output to a +3V3 power symbol through another 10 µF capacitor (C8).

  3. Add power symbols. Place +5V, +3V3, and GND power symbols. The MCU runs on +5V; the sensors run on +3V3.

  4. Wire the power section. USB VBUS to +5V, USB GND to GND. LDO input from +5V, LDO output to +3V3.

MCU and Crystal

  1. Place the ATmega328P-AU. Add ATmega328P-AU (the TQFP-32 variant). Place it in the center of the schematic.

  2. Connect power. Wire VCC (pin 4), AVCC (pin 18) to +5V. Wire GND (pins 3, 5, 21) to GND. Place 100 nF decoupling capacitors (C3, C4) close to VCC and AVCC.

  3. Crystal circuit. Place a Crystal_SMD_4Pin (16 MHz) between XTAL1 (pin 7) and XTAL2 (pin 8). Add 22 pF load capacitors (C1, C2) from each crystal pin to GND.

  4. Reset circuit. Connect a 10 kΩ pull-up (R1) from RESET (pin 29) to +5V. Add the SMD tactile switch from RESET to GND.

  5. ISP header. Place Conn_AVR_ISP and wire MISO, MOSI, SCK, RESET, VCC, GND as in Lesson 1.

I2C Bus and Sensors

Both the SHT30 and VEML7700 communicate over I2C, so they share the same bus.

  1. I2C pull-ups. Add two 4.7 kΩ resistors (R2, R3). Connect R2 from SDA (PC4/pin 27) to +3V3. Connect R3 from SCL (PC5/pin 28) to +3V3. We pull up to 3.3V because both sensors are 3.3V devices. The ATmega328P I2C pins are 3.3V tolerant when running at 5V.

  2. Place the SHT30. Add SHT3x-DIS (or create a custom symbol with VDD, GND, SDA, SCL, ADDR, ALERT pins). Connect VDD to +3V3, GND to GND, SDA to the I2C SDA bus, SCL to the I2C SCL bus. Tie ADDR to GND for address 0x44. Add a 100 nF decoupling cap (C5).

  3. Place the VEML7700. Add VEML7700 (or a generic I2C sensor symbol). Connect VDD to +3V3, GND to GND, SDA and SCL to the I2C bus. The VEML7700 has a fixed I2C address (0x10), so no address pin is needed. Add a 100 nF decoupling cap (C6).

UART Output

  1. Place a 1x4 pin header (J3) for UART output. Pins: VCC, TX, RX, GND. This connects to a USB-UART adapter (like FTDI or CP2102) for serial data output.

  2. Wire UART. Connect ATmega328P TXD (PD1/pin 31) to the TX pin and RXD (PD0/pin 30) to the RX pin on the header.

Power LED

  1. Place an 0805 LED (D1) and 330 Ω resistor (R4). Connect +3V3 to R4, R4 to D1 anode, D1 cathode to GND. This indicates the 3.3V rail is active.

  2. Run ERC. Fix any errors before proceeding.

Assign Footprints

Open Tools > Assign Footprints and set:

SymbolFootprint
ATmega328P-AUPackage_QFP:TQFP-32_7x7mm_P0.8mm
AMS1117-3.3Package_TO_SOT_SMD:SOT-223-3_TabPin2
SHT3x-DISSensor_Humidity:Sensirion_DFN-8-1EP_2.5x2.5mm_P0.5mm_EP1.1x1.7mm
VEML7700OptoDevice:Vishay_OPLGA-4_1.25x1mm_P0.8mm
Crystal 16MHzCrystal:Crystal_SMD_3215-4Pin_3.2x1.5mm
USB_B_MicroConnector_USB:USB_Micro-B_Molex_47346-0001
C (0805)Capacitor_SMD:C_0805_2012Metric
R (0805)Resistor_SMD:R_0805_2012Metric
LED (0805)LED_SMD:LED_0805_2012Metric
SW_PushButton_Switch_SMD:SW_SPST_PTS810
Conn_AVR_ISPConnector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical
Conn_01x04Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical

PCB Layout



Board Setup

  1. Board outline. On the Edge.Cuts layer, draw a 40 x 35 mm rectangle.

  2. Layer setup. This is a double-sided board: F.Cu (front copper) and B.Cu (back copper). Go to Board Setup > Board Stackup > Physical Stackup and verify the board has two copper layers with a 1.6mm substrate.

  3. Design rules. Set the Default net class:

    • Track width: 0.25 mm (for signal traces between TQFP pads)
    • Clearance: 0.2 mm
    • Via size: 0.6 mm, drill 0.3 mm

    Add a Power net class:

    • Track width: 0.5 mm
    • Clearance: 0.2 mm

Import and Place Components

  1. Update PCB from schematic (F8). All components appear in a cluster.

  2. Place the USB connector on the top edge of the board, centered, with the plug side extending slightly beyond the board edge.

  3. Place U2 (LDO) near the USB connector. Put C7 and C8 immediately adjacent to its input and output pins.

  4. Place U1 (ATmega328P-AU) in the center. The TQFP package is compact, so component placement around it matters more.

  5. Place the crystal (Y1) as close to pins 7 and 8 as possible. Place C1 and C2 right next to the crystal.

  6. Place decoupling caps (C3, C4) within 3mm of the VCC/AVCC pins. Shorter is better.

  7. Place sensors. Put U3 (SHT30) away from heat sources (the LDO, MCU). Temperature sensors need accurate ambient readings. Put U4 (VEML7700) near a board edge where it can see ambient light.

  8. Place the ISP header (J2) and UART header (J3) on the bottom edge for easy access.

  9. Place the reset button near a board edge.

Routing Strategy

  1. Route power traces first using the Power net class (0.5mm). Run +5V and +3V3 as short, direct traces from their sources to each component.

  2. Route the crystal traces. Keep XTAL1 and XTAL2 short, symmetric, and away from other signals. Guard them with a ground pour later.

  3. Route I2C bus. SDA and SCL run from the MCU to both sensors and the pull-up resistors. Keep these traces under 50mm total length. Route them as a pair with consistent spacing.

  4. Route UART. TX and RX from the MCU to the UART header. These are low-speed signals, so routing is straightforward.

  5. Route ISP signals. MISO, MOSI, SCK, RESET from the MCU to the ISP header.

  6. Use vias when needed. With a double-sided board, you can route signals on the back copper layer. Press V during routing to place a via and switch layers. Keep via count reasonable (under 20 for this board).

  7. Add ground pours on both layers. Create a GND zone on F.Cu and B.Cu. Fill them with B (Build All Zones). This provides a solid ground plane and reduces the amount of copper that needs to be removed during milling.

  8. Run DRC. Zero errors, zero unconnected items.

Double-Sided Layout Tips

SMD Placement Rules

  • Keep all SMD components on the front side unless space forces some to the back
  • Through-hole components (ISP header, UART header) can go on either side
  • If a component must go on the back, flip it with F key in KiCad
  • The sensor ICs (SHT30, VEML7700) should face outward, not trapped between the board and a surface

Manufacturing: CNC Milling



CNC milling removes copper mechanically instead of chemically. A small end mill cuts isolation channels around traces, leaving the copper traces as raised islands. This works well for double-sided boards because you can flip the board and mill the other side.

Why CNC Milling?

  • No chemicals (no ferric chloride, no disposal issues)
  • Higher precision than toner transfer (0.1mm channels are achievable)
  • Repeatable (once set up, you can make identical boards)
  • Drills holes in the same setup
  • Works well for fine-pitch SMD pads like TQFP

CNC Milling Workflow

  1. Export Gerber files. In KiCad PCB Editor, go to File > Plot. Select F.Cu, B.Cu, and Edge.Cuts layers. Set format to Gerber. Click Plot. Then click Generate Drill Files in Excellon format.

  2. Import into your CNC software. Common options:

    • FlatCAM (free, open source): imports Gerbers and generates G-code
    • Candle or bCNC: G-code senders for GRBL-based CNC machines
    • PCB-GCODE (Eagle plugin also works with Gerber conversions)
  3. Set milling parameters in FlatCAM:

    • V-bit: 0.1mm tip, 30-degree angle
    • Isolation width: 0.15mm (number of passes: 2-3 for wider isolation)
    • Feed rate: 100-200 mm/min
    • Cut depth: 0.05-0.1mm (just deep enough to cut through the copper layer, typically 35 µm thick)
  4. Secure the copper-clad board to the CNC bed. Use double-sided tape or a vacuum table. The board must be perfectly flat; any warping will cause uneven cuts.

  5. Set the Z-origin (tool height). Touch the bit to the board surface. A Z-probe or continuity tester helps find the exact surface. This is the most critical setup step: too shallow and traces short together, too deep and the bit wears out fast.

  6. Mill the front copper layer. Run the F.Cu isolation routing G-code. The CNC cuts channels around every pad and trace, leaving copper islands.

  7. Drill all holes. Switch to a 0.8mm drill bit and run the Excellon drill file. The holes serve as alignment points when you flip the board.

  8. Flip the board for the back layer. Use two drilled holes as alignment pins. Secure the board face-down. Re-probe the Z-height.

  9. Mill the back copper layer. Run the B.Cu isolation routing G-code.

  10. Mill the board outline. Switch to a 1-2mm end mill and run the Edge.Cuts G-code to cut the board to its final shape.

  11. Clean and inspect. Remove copper dust with a brush. Inspect under magnification: check that all isolation channels are clean and no copper bridges remain between pads.

Connecting Front and Back Layers (Manual Vias)

CNC-milled boards do not have plated through-holes. You need to create manual vias:

  1. For each via, insert a short piece of bare copper wire (0.5mm diameter) through the drilled hole.

  2. Solder the wire to the pad on the front side, trim it, then flip the board and solder it to the pad on the back side.

  3. Trim excess wire flush with the solder joints.

This adds time compared to professionally fabricated boards, but it works reliably for prototypes.

Soldering SMD Components



SMD soldering requires different tools than through-hole work.

Tools Needed

ToolPurpose
Fine-tip soldering iron (0.5mm tip)Resistors, capacitors, LED, headers
Solder paste (Sn63/Pb37 or lead-free)TQFP and sensor ICs
Hot air stationReflowing solder paste on TQFP and sensors
Flux penImproving solder flow on fine-pitch pads
Tweezers (fine-point)Placing 0805 components
Magnifying glass or microscopeInspecting solder joints

Soldering Order

  1. TQFP MCU first. Apply solder paste to the TQFP-32 pads. Place the ATmega328P-AU with tweezers, aligning pin 1 with the pad 1 mark. Reflow with hot air at 250-280°C, holding the nozzle 2-3 cm above the chip. The paste will melt and self-align the chip. Inspect every pin under magnification for solder bridges.

  2. Sensor ICs. Apply paste and reflow the SHT30 and VEML7700 the same way. The SHT30 has an exposed pad on the bottom; make sure the paste connects it to the ground pad.

  3. LDO and crystal. Solder the SOT-223 regulator and SMD crystal. These are larger and easier than the sensors.

  4. 0805 passives. Tin one pad of each resistor/capacitor location with a small amount of solder. Place the component with tweezers, reflow the tinned pad, then solder the other pad. This “tack and solder” method is fast for 0805 parts.

  5. USB connector. Solder the Micro-B connector. The shield pins provide mechanical strength; solder them well.

  6. Through-hole headers. Solder the ISP and UART pin headers last, as they are the tallest components.

  7. Manual vias. If you used CNC milling, solder all via wires now.

Common SMD Mistakes

Troubleshooting

  • Solder bridges on TQFP: drag a clean, fluxed iron tip across the pins to wick away excess solder. Or use solder wick.
  • Tombstoning (0805 part stands up on one end): uneven solder paste or uneven heating. Remove, clean, and resolder.
  • Cold joints: dull, grainy appearance. Reheat with flux.
  • Component shifted during reflow: the component moved when you applied hot air. Use less airflow, or tack one side with an iron first.

First Power-On and Testing



  1. Visual inspection. Check every solder joint under magnification. Look for bridges on the TQFP pins especially.

  2. Continuity check. Verify no short between +5V and GND, or +3V3 and GND.

  3. Apply power via USB. Plug in a USB cable. The power LED should light. Measure +5V at the MCU VCC pin and +3.3V at the sensor VDD pins.

  4. Flash the blink firmware from Lesson 1 over the ISP header. If the LED blinks, the MCU and crystal are working.

Firmware: Reading Sensors



Save this as sensor_read.c. It reads both I2C sensors and outputs data over UART at 9600 baud.

/*
* sensor_read.c - Read SHT30 and VEML7700 via I2C, output via UART
* ATmega328P-AU @ 16 MHz
*/
#define F_CPU 16000000UL
#define BAUD 9600
#define UBRR_VAL ((F_CPU / 16 / BAUD) - 1)
#include <avr/io.h>
#include <util/delay.h>
#include <util/twi.h>
#include <stdio.h>
/* UART */
void uart_init(void) {
UBRR0H = (uint8_t)(UBRR_VAL >> 8);
UBRR0L = (uint8_t)UBRR_VAL;
UCSR0B = (1 << TXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
void uart_putc(char c) {
while (!(UCSR0A & (1 << UDRE0)));
UDR0 = c;
}
void uart_puts(const char *s) {
while (*s) uart_putc(*s++);
}
/* I2C */
void i2c_init(void) {
TWSR = 0;
TWBR = ((F_CPU / 100000UL) - 16) / 2; /* 100 kHz */
}
uint8_t i2c_start(uint8_t addr) {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
TWDR = addr;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
return (TWSR & 0xF8);
}
void i2c_write(uint8_t data) {
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
}
uint8_t i2c_read_ack(void) {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
while (!(TWCR & (1 << TWINT)));
return TWDR;
}
uint8_t i2c_read_nack(void) {
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
return TWDR;
}
void i2c_stop(void) {
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
}
/* SHT30 (I2C address 0x44) */
void sht30_read(int16_t *temp_raw, uint16_t *hum_raw) {
uint8_t data[6];
/* Send single-shot measurement command: 0x2400 (high repeatability) */
i2c_start(0x44 << 1);
i2c_write(0x24);
i2c_write(0x00);
i2c_stop();
_delay_ms(50); /* Wait for measurement */
/* Read 6 bytes: temp_msb, temp_lsb, temp_crc, hum_msb, hum_lsb, hum_crc */
i2c_start((0x44 << 1) | 1);
for (uint8_t i = 0; i < 5; i++) data[i] = i2c_read_ack();
data[5] = i2c_read_nack();
i2c_stop();
*temp_raw = (data[0] << 8) | data[1];
*hum_raw = (data[3] << 8) | data[4];
}
/* VEML7700 (I2C address 0x10) */
uint16_t veml7700_read(void) {
uint8_t low, high;
/* Configure: ALS gain x1, integration time 100ms, power on */
i2c_start(0x10 << 1);
i2c_write(0x00); /* ALS_CONF register */
i2c_write(0x00); /* LSB: power on, gain x1 */
i2c_write(0x00); /* MSB: integration 100ms */
i2c_stop();
_delay_ms(150); /* Wait for measurement */
/* Read ALS data register (0x04) */
i2c_start(0x10 << 1);
i2c_write(0x04);
i2c_start((0x10 << 1) | 1);
low = i2c_read_ack();
high = i2c_read_nack();
i2c_stop();
return (high << 8) | low;
}
/* Simple integer-to-string for UART output */
void print_int(int16_t val) {
char buf[8];
int8_t i = 0;
if (val < 0) { uart_putc('-'); val = -val; }
if (val == 0) { uart_putc('0'); return; }
while (val > 0) { buf[i++] = '0' + (val % 10); val /= 10; }
while (i > 0) uart_putc(buf[--i]);
}
int main(void) {
int16_t temp_raw;
uint16_t hum_raw;
uint16_t lux_raw;
uart_init();
i2c_init();
uart_puts("Sensor Shield Ready\r\n");
while (1) {
/* Read SHT30 */
sht30_read(&temp_raw, &hum_raw);
int16_t temp_c = -45 + (175 * (int32_t)temp_raw / 65535);
uint16_t hum_pct = 100 * (uint32_t)hum_raw / 65535;
/* Read VEML7700 */
lux_raw = veml7700_read();
/* Output */
uart_puts("T=");
print_int(temp_c);
uart_puts("C H=");
print_int(hum_pct);
uart_puts("% L=");
print_int(lux_raw);
uart_puts(" lux\r\n");
_delay_ms(2000);
}
}

Compile and Flash

Terminal window
avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -Os -o sensor_read.elf sensor_read.c
avr-objcopy -O ihex sensor_read.elf sensor_read.hex
avrdude -c usbasp -p m328p -U flash:w:sensor_read.hex:i

Connect a USB-UART adapter (FTDI or CP2102) to the UART header. Open a serial terminal at 9600 baud. You should see readings like:

Sensor Shield Ready
T=23C H=45% L=312 lux
T=23C H=45% L=310 lux

What You Have Learned



Lesson 2 Complete

KiCad skills:

  • Worked with SMD footprints (TQFP, 0805, SOT-223, DFN)
  • Designed a double-sided PCB with front and back copper layers
  • Used vias to route signals between layers
  • Set up fine-pitch design rules for TQFP routing

Electronics skills:

  • I2C bus design with pull-up resistors and multiple devices
  • Dual voltage rail design (5V MCU + 3.3V sensors)
  • SMD component selection and sizing (0805 passives)

Manufacturing skills:

  • CNC milling workflow (Gerber to FlatCAM to G-code)
  • Double-sided alignment technique with drilled reference holes
  • Manual via soldering for non-plated boards

Firmware skills:

  • Bare-metal I2C (TWI) driver for ATmega328P
  • SHT30 temperature/humidity sensor communication
  • VEML7700 ambient light sensor communication
  • UART serial output for sensor data

Comments

Loading comments...


© 2021-2026 SiliconWit®. All rights reserved.