This week, I focused on designing, fabricating, and assembling a custom PCB. I used KiCad for schematic and PCB design, then fabricated the board using milling and laser etching, and finally assembled and tested it.
Learning Objectives
Select and use software for circuit board design
Demonstrate workflows used in circuit board design
Assignments
Group Assignments
Send a message between two projects
Document your work to the group work page and reflect on your individual page what you learned
The group assignment was to communicate between any 2 projects already made. For the assignment purpose, we communicated between my and Ancy's project. The commnication between the two projects was done by ESP-NOW as both are boards were XIAO ESP32s. You can find the group assignment on the below page.
Generally a microcontroller will have separate serial communication modules with different pinouts for
each module. Separate dedicated peripherals and user registers will be available for each module. For
example, USART will be a separate peripheral with dedicated pins for its function and I2C will be a
separate peripheral with its own dedicated pins.
In SAM D microcontrollers, all the serial peripherals are designed into a single module as serial
communication interface (SERCOM). A SERCOM module can be configured either as USART, I2C, or
SPI, selectable by the user. Each SERCOM will be assigned four pads from PAD0 to PAD3. The
functionality of each pad is configurable depending on the SERCOM mode used. Unused pads can be
used for other purposes and the SERCOM module will not control them unless they are configured to be
used by the SERCOM module.
For example, SERCOM0 can be configured as USART mode with PAD0 as transmit pad and PAD1 as
receive pad. Other unused pads (PAD2 and PAD3) can be used either as GPIO pins or be assigned to
some other peripherals. The assignment of SERCOM functionality for different pads is highly flexible
making the SERCOM module more advantageous compared to the typical serial communication
peripheral implementation
I used this library for my SAMD21 Borad to communicate it as a slave
SAMD21 SPI Slave Code
I took this example code from the above given github repository to initialize the SAMD21 Board as the Slave in the SPI Communication. I have edited the code for the correct pins in my case for my SAMD21 Board. You can find the detailed documentation of the TouchNav SAMD21 Board in the input week assignment.
/*
Copyright (C) 2022 lenvm
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
For the GNU General Public License see https://www.gnu.org/licenses/
Contact Information
-------------------
lenvm
GitHub : https://github.com/lenvm
*/
/*
Example code for the SercomSPISlave library.
This code initializes a SERCOM1 SPI Slave and prints the data received.
Written 2020 July 15
by lenvm
Updated 2021 June 8
by lenvm
Updated 2022 November 1
by lenvm
*/
#include <SercomSPISlave.h>
Sercom1SPISlave SPISlave; // to use a different SERCOM, change this line and find and replace all SERCOM1 with the SERCOM of your choice
#define DEBUG // uncomment this line to print debug data to the serial bus
#define INTERRUPT2BUFFER // uncomment this line to copy the data received in the Data Received Complete interrupt to a buffer to be used in the main loop
//#define INTERRUPT2SERIAL // uncomment this line to print the data to the serial bus whenever the Data Received Complete interrupt is triggered
// initialize variables
byte buf[1]; // initialize a buffer of 1 byte
void setup()
{
Serial.begin(115200);
Serial.println("Serial started");
SPISlave.SercomInit(SPISlave.MOSI_Pins::PA16, SPISlave.SCK_Pins::PA17, SPISlave.SS_Pins::PA18, SPISlave.MISO_Pins::PA19);
Serial.println("SERCOM1 SPI slave initialized");
}
void loop()
{
#ifdef INTERRUPT2BUFFER
Serial.println(buf[0]); // Print latest data written into the buffer by the interrupt
delay(1); // Delay of 1 ms
#endif
#ifdef INTERRUPT2SERIAL
delay(1000); // Delay of 1 s to keep the main loop running, while data is written to the serial every time the Data Received Interrupt is triggered
#endif
}
void SERCOM1_Handler()
/*
Reference: Atmel-42181G-SAM-D21_Datasheet section 26.8.6 on page 503
*/
{
#ifdef DEBUG
Serial.println("In SPI Interrupt");
#endif
uint8_t data = 0;
data = (uint8_t)SERCOM1->SPI.DATA.reg;
uint8_t interrupts = SERCOM1->SPI.INTFLAG.reg; // Read SPI interrupt register
#ifdef DEBUG
Serial.print("Interrupt: "); Serial.println(interrupts);
#endif
// Slave Select Low interrupt
if (interrupts & (1 << 3)) // 1000 = bit 3 = SSL // page 503
{
#ifdef DEBUG
Serial.println("SPI Slave Select Low interupt");
#endif
SERCOM1->SPI.INTFLAG.bit.SSL = 1; // Clear Slave Select Low interrupt
}
// Data Received Complete interrupt: this is where the data is received, which is used in the main loop
if (interrupts & (1 << 2)) // 0100 = bit 2 = RXC // page 503
{
#ifdef DEBUG
Serial.println("SPI Data Received Complete interrupt");
#endif
data = SERCOM1->SPI.DATA.reg; // Read data register
SERCOM1->SPI.INTFLAG.bit.RXC = 1; // Clear Receive Complete interrupt
}
// Data Transmit Complete interrupt
if (interrupts & (1 << 1)) // 0010 = bit 1 = TXC // page 503
{
#ifdef DEBUG
Serial.println("SPI Data Transmit Complete interrupt");
#endif
SERCOM1->SPI.INTFLAG.bit.TXC = 1; // Clear Transmit Complete interrupt
}
// Data Register Empty interrupt
if (interrupts & (1 << 0)) // 0001 = bit 0 = DRE // page 503
{
#ifdef DEBUG
Serial.println("SPI Data Register Empty interrupt");
#endif
SERCOM1->SPI.DATA.reg = 0xAA;
}
#ifdef INTERRUPT2BUFFER
// Write data to buffer, to be used in main loop
buf[0] = data;
#endif
#ifdef INTERRUPT2SERIAL
// Print data received during the Data Receive Complete interrupt
char _data = data;
Serial.print("DATA: ");
Serial.println(_data); // Print received data
#endif
}
ESP32 Master Code
This is the master code for the XIAO ESP32C6 Board that I used for communicating with the TouchNav SAMD21 Board. I used ChatGPT to write this code.
ChatGPT Prompt: I want to communicate between a SAMD21 and a XIAO ESP32-C6 using SPI. The ESP32-C6 will act as the SPI master and send integer data to the SAMD21 (SPI slave). The data should be sent when I type a number into the Serial Monitor and press Enter.
#include
#define SCK D8
#define MISO D10
#define MOSI D9
#define CS D0
void setup() {
Serial.begin(115200);
SPI.begin(SCK, MISO, MOSI, CS);
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
}
void loop() {
delay(1000); // Every 1 sec send data
if (Serial.available()) {
String inputString = Serial.readStringUntil('\n'); // read till Enter key
int response = inputString.toInt(); // convert to integer
digitalWrite(CS, LOW);
SPI.transfer(response); // Send 0x55 and read response
Serial.print("You entered: ");
Serial.println(response);
digitalWrite(CS, HIGH);
}
}
Individual Assignments
NFC + Motor Board
I wanted to design a board which used RFID communication as it was required for my final project. This week was the best time to try the MRFC522 RFID Reader Module. I also wanted to control a motor according to the RFID Input. This would help me to integrate in the final project.
I also wanted to try using my boards to communicate using Morse Code. That's why I added a push button and a speaker/buzzer to the board.
Designing the board
Components Used
RFID RC522 Dev Kit
DC Motor
Speaker
Push Button
WS2812B LED
RFID-RC522 Development Kit
This RC522 RFID Development kit is based on NXP's a highly integrated reader/writer IC MFRC522 for
contactless communication at 13.56 MHz. The MFRC522 reader supports ISO/IEC 14443 A/MIFARE and
NTAG. The MFRC522's internal transmitter is able to drive a reader/ writer antenna designed to
communicate with ISO/IEC 14443A cards and transponders without additional active circuitry. The receiver
module provides a robust and efficient implementation for demodulating and decoding signals from
ISO/IEC 14443A compatible cards and transponders.
Supports ISO/IEC 14443A higher transfer speed communication up to 848 KBd.
SPI bus speed up to 10Mbit/s.
I2C-bus interface up to 400 kBd in Fast mode, up to 3400 kBd in High-speed mode.
RS232 Serial UART up to 1228.8 kBd, with voltage levels dependant on pin voltage
supply.
Compatible with MIFARE and ISO 14443A cards.
Typical operating distance in Read/Write mode up to 50 mm depending on the antenna
size and tuning.
I used the SPI protocol to communicate with the RFID RC522 Development Board as it offered maximum speed. I have used a 1x8 Through Hole Header Array to connect the RC522 Board.
Schematic RC522
DC Motor with N Channel Mosfet
For my final project, I need a motor to rotate the record to mimic the turntable effect of the classic Vinyl Record Player. I didn't need any complex motor control, so I decide to make a simple DC Motor circuit driven by a simple N Channel Mosfet.
I refered to Akash's documentation to design my motor driver circuit
DC Motor with N Channel MOSFET
Speaker
For this week's assignment , I wanted to make a Wifi based Morse Code communication board. For a typical Morse Code Machine the sound is very essential, that's why I wanted to use a speaker/buzzer for the board. So I decide to use the speaker that Namita used for her last week's assignment as it required a simple driver circuit which is MOSFET based unlike my I2S DAC+Amplifier Based Circuit which used for my assignment last week. The AST0927MW-3.6Q Speaker is an electromagnetic transducer which is specifically designed for audio indication, with a resonant frequency of 2.73 kHz and a sound pressure level (SPL) of 85 dB. I used the footprint provided by Namita which she made last week. You can refer to her documentation for more information about the speaker and how she made the footprint.
The footprint of the AST0927MW-3.6Q Speaker is attached here - Speaker Footprint
The speaker also uses a N Channel Mosfet as a driver. I have attached a Red LED to the Speaker-In pin through a resistor, this makes it light up by default everytime the speaker is on, making it easier for me. I didn't have to use a pin or program the LED Separately.
Speaker - N Channel Mosfet Driver Circuit
WS2812B and Push Button
The WS2812B Addressable RGB LED is also used to signify the status of the board and the push button is used to send morse code messages. The push button is pulled up hy by a resistor to avoid floating values.
NFC Motor Board Schematic
The schematic and PCB design was doen in the KiCad Software.
NFC XIAO ESP32C6 Board Schematic
NFC Motor Board PCB Design
This is the PCB Design of the board. I edited the trace width of the 12V and GND lines to carry more current. The trace width of those power lines is 0.8mm whiile rest of the trace widths are 0.4 mm.
NFC XIAO ESP32C6 Board: PCB Design
PCB Milling using Milling Machine
I used the Modela RDX Milling Machine for milling the PCB
Traces milled
The traces were clean and the board was ready to be assembled using all the components.
Assembly
I setup the board on the PCB holder setup and requested all the components from the Fab Stash and collected it from the inventory.
After soldering all the components on the PCB, I attached the motor and the RC522 RFID Reader Module to the board. Below are some hero shots of the board.
After the milling the board on the Modela RDX Milling machine, I wanted to try using another process for my next PCB. I used the XTools F1 Ultra for my next board.
You can refer to the above documentation on the process of PCB engraving using the XTools F1 Ultra in the 'Using the XTool F1 Ultra for PCB Engraving' section.
You can see the video of the Xtools engraving the FR4 Board. It is not possible to engrave on the FR1 board as the board would burn eventually in the process. The FR4 is heat resistant as compared to the FR1.
The output looks really nice with really tight tolerances and neat traces as compared to the milling process
I tested the NFC Module using the example code from the library. You need to install the MFRC522 library by Github to use this code.
The library and the step by step process is mentioned in the documentation
The example is called "ReadNUID" in the MFRC522 library. It will read the NUID of the MIFARE Classic Tag and print it to the Serial Monitor. The code is given below.
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN D7
#define RST_PIN D6
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class
// Init array that will store new NUID
byte nuidPICC[4];
void setup() {
Serial.begin(9600);
SPI.begin(); // Init SPI bus
rfid.PCD_Init(); // Init MFRC522
Serial.println(F("This code scan the MIFARE Classsic NUID."));
}
void loop() {
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( ! rfid.PICC_IsNewCardPresent())
return;
// Verify if the NUID has been readed
if ( ! rfid.PICC_ReadCardSerial())
return;
Serial.print(F("PICC type: "));
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
Serial.println(rfid.PICC_GetTypeName(piccType));
// Check is the PICC of Classic MIFARE type
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
if (rfid.uid.uidByte[0] != nuidPICC[0] ||
rfid.uid.uidByte[1] != nuidPICC[1] ||
rfid.uid.uidByte[2] != nuidPICC[2] ||
rfid.uid.uidByte[3] != nuidPICC[3] ) {
Serial.println(F("A new card has been detected."));
// Store NUID into nuidPICC array
for (byte i = 0; i < 4; i++) {
nuidPICC[i] = rfid.uid.uidByte[i];
}
Serial.println(F("The NUID tag is:"));
Serial.print(F("In hex: "));
printHex(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
Serial.print(F("In dec: "));
printDec(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
}
else Serial.println(F("Card read previously."));
// Halt PICC
rfid.PICC_HaltA();
// Stop encryption on PCD
rfid.PCD_StopCrypto1();
}
/**
* Helper routine to dump a byte array as hex values to Serial.
*/
void printHex(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
/**
* Helper routine to dump a byte array as dec values to Serial.
*/
void printDec(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(' ');
Serial.print(buffer[i], DEC);
}
}
I wanted to try something basic for my final project progress. So I made the motor run when the NFC ID was detected and to stop the motor when the ID was detected again
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN D7
#define RST_PIN D6
#define motor D1
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class
// Init array that will store new NUID
byte nuidPICC[4];
void setup() {
pinMode(motor,OUTPUT);
Serial.begin(9600);
SPI.begin(); // Init SPI bus
rfid.PCD_Init(); // Init MFRC522
Serial.println(F("This code scan the MIFARE Classsic NUID."));
}
void loop() {
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( ! rfid.PICC_IsNewCardPresent())
return;
// Verify if the NUID has been readed
if ( ! rfid.PICC_ReadCardSerial())
return;
Serial.print(F("PICC type: "));
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
Serial.println(rfid.PICC_GetTypeName(piccType));
// Check is the PICC of Classic MIFARE type
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("Your tag is not of type MIFARE Classic."));
return;
}
if (rfid.uid.uidByte[0] != nuidPICC[0] ||
rfid.uid.uidByte[1] != nuidPICC[1] ||
rfid.uid.uidByte[2] != nuidPICC[2] ||
rfid.uid.uidByte[3] != nuidPICC[3] ) {
Serial.println(F("A new card has been detected."));
analogWrite(motor, 30);
// Store NUID into nuidPICC array
for (byte i = 0; i < 4; i++) {
nuidPICC[i] = rfid.uid.uidByte[i];
}
Serial.println(F("The NUID tag is:"));
Serial.print(F("In hex: "));
printHex(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
Serial.print(F("In dec: "));
printDec(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
}
else
{
Serial.println(F("Card read previously."));
analogWrite(motor, 0);
}
// Halt PICC
rfid.PICC_HaltA();
// Stop encryption on PCD
rfid.PCD_StopCrypto1();
}
/**
* Helper routine to dump a byte array as hex values to Serial.
*/
void printHex(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
/**
* Helper routine to dump a byte array as dec values to Serial.
*/
void printDec(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(' ');
Serial.print(buffer[i], DEC);
}
}
MQTT Communication with Rico
Using the board that I designed in this week, I communicated with Rico Kanthatham at Skylab Workshop. I used Message Query Telemetry Transportation (MQTT) for this purpose. I have documented about MQTT and the whole process in the week 14 Interface and Application assignment. You can refer to the same.
This is the video of Rico sending and recieving the morse code to our lab