I am an undergraduate of Electrical Engineering who is completing my final semester at the University of Victoria and will be beginning work as an EIT in January 2023.
Beyond school and engineering, I enjoy hanging out with friends, lifting weights, cooking, and working on computer projects such as making this website or working on my media server. Other than that, people have joked by saying that my hobby is finding new hobbies and that is actually somewhat true. I have tried many different things with varying success as to what sticks. Some examples of hobbies that stuck are
Linux system customization/"ricing" on Arch Linux (overall, a lot of shell scripting and playing around with colour schemes)
Electronics (started in high school and evolved into much more than just a hobby)
Self-hosting (on the server that hosts this website, I also host my own cloud storage, email server, and password vault)
Weightlifting (currently at 1105lb total with 450lb squat, 405lb deadlift, and 250lb bench)
Web development (see this website)
Bread making (can make a good sourdough)
Meditation (first thing I do every day)
Some examples of things I tried that didn't stick (but hope to try again one day) are
Music production (made it halfway through a Udemy course on music theory)
Guitar (Didn't get much further than learning the Seven Nation Army riff)
Game development (went through some Unity tutorials and learned basic C# but never made my own game)
Getting flexible enough to do the splits (got close though)
Overall, my philosophy is that whether or not things stick, it is important to try new things because no matter what, you will learn something new and sometimes little things like these can turn into a passion.
Autonomous Underwater Vehicles Interdisciplinary Club (AUVIC) Projects
AUVIC is a University of Victoria robotics team that builds Autonomous Underwater Vehicles (AUVs) to compete in the international RoboSub competition. I joined AUVIC in September 2017–the first semester of my first year of university. Since then, I have been the team President, Project Manager, and Electrical Lead for three, two, and two semesters respectively. Currently, I am serving as the Project Manager and Electrical team lead. On top of this, I am working on designing a new motor controller PCB, IMU breakout board, and motor controller firmware.
The Motor Controller Project (Soldering PCB)
In this project, I am designing a motor controller board that controls the speed of eight brushless DC (BLDC) motors. The end goal is a board that receives commands from a Nvidia Jetson (embedded Linux system that serves as the AUV's main computer) through CANbus and controls motors accordingly. Currently, schematics and routing are complete and the board is being fabricated.
Motor Controller Overview:
Motor Controller Microcontroller Schematic:
Motor Controller Power Schematic:
Motor Controller CAN Schematic:
Motor Controller UART Schematic (for debugging):
Motor Controller ESC Schematic (repeated 8 times):
Motor Controller Sensors Schematic (temperature and current):
IMU Breakout Board
The IMU Breakout Board is a PCB I designed to communicate with the club's new Xsens MTi-620 VRU. The goal of this board was to breakout the MTi-620's phoenix connector to a more accessible 16*2 male header and AUVIC's standard power and CAN connectors. The MTi-620 will mount to the board using surface mount standoffs.
IMU Breakout Board Schematic:
IMU Breakout Board PCB:
IMU Breakout Board Completed:
Motor Controller Firmware
Writing C firmware for the motor controller was the first task I did when I joined AUVIC and it is likely how I will spend my last year with AUVIC after the motor controller is complete. All the code I wrote can be found on the AUVIC Github. However, this is a shared repository with multiple contributors so, in the following sections, I will display code that was written solely by myself.
Multiplexing Firmware
The multiplexing firmware was the first project I completed with AUVIC. Since the microcontroller had limited pins and we had to read data from 16 sensors, my task was to write a program to control a 16x1 multiplexer to read from all 16 sensors using only one analog input and four control outputs.
extern void set_motor_current_temp_MUX(ADC_sensors_t ADC_sensor_x) {
switch (ADC_sensor_x) {
case Curr_ADC1:
GPIOC->ODR |= GPIO_Pin_9; //turn on PC9
GPIOC->ODR &= ~(GPIO_Pin_7 | GPIO_Pin_8);
break;
case Curr_ADC2:
GPIOC->ODR |= GPIO_Pin_9 | GPIO_Pin_8; //turn on PC9 and PC8
GPIOC->ODR &= ~(GPIO_Pin_7);
break;
case Curr_ADC3:
GPIOC->ODR |= GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_7; //turn on PC9, PC8, and PC7
break;
case Curr_ADC4:
GPIOC->ODR |= GPIO_Pin_9 | GPIO_Pin_7; //turn on PC9 and PC7
GPIOC->ODR &= ~(GPIO_Pin_8);
break;
case Curr_ADC5:
GPIOD->ODR |= GPIO_Pin_9; //turn on PD9
GPIOD->ODR &= ~(GPIO_Pin_8);
GPIOB->ODR &= ~(GPIO_Pin_15);
break;
case Curr_ADC6:
GPIOD->ODR |= GPIO_Pin_9 | GPIO_Pin_8; //turn on PD9 and PD8
GPIOB->ODR &= ~(GPIO_Pin_15);
break;
case Curr_ADC7:
GPIOD->ODR |= GPIO_Pin_9 | GPIO_Pin_8; //turn on PD9 and PD8
GPIOB->ODR |= GPIO_Pin_15; //turn on PB15
break;
case Curr_ADC8:
GPIOD->ODR |= GPIO_Pin_9; //turn on PD9
GPIOB->ODR |= GPIO_Pin_15; //turn on PB15
GPIOD->ODR &= ~(GPIO_Pin_8);
break;
case Temp_ADC1:
GPIOC->ODR |= GPIO_Pin_8; //turn on PC8
GPIOC->ODR &= ~(GPIO_Pin_9 | GPIO_Pin_7);
break;
case Temp_ADC2:
GPIOC->ODR |= GPIO_Pin_7; //turn on PC7
GPIOC->ODR &= ~(GPIO_Pin_8 | GPIO_Pin_9);
break;
case Temp_ADC3:
GPIOC->ODR &= ~(GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_7); //turn off PC7-PC9
break;
case Temp_ADC4:
GPIOC->ODR |= GPIO_Pin_8 | GPIO_Pin_7; //turn on PC7-PC8
GPIOC->ODR &= ~(GPIO_Pin_9);
break;
case Temp_ADC5:
GPIOD->ODR |= GPIO_Pin_8; //turn on PD8
GPIOD->ODR &= ~(GPIO_Pin_9);
GPIOB->ODR &= ~(GPIO_Pin_15);
break;
case Temp_ADC6:
GPIOB->ODR |= GPIO_Pin_15; //turn on PB15
GPIOD->ODR &= ~(GPIO_Pin_8 | GPIO_Pin_9);
break;
case Temp_ADC7:
GPIOD->ODR &= ~(GPIO_Pin_8 | GPIO_Pin_9); //turn off PD8-PD9
GPIOB->ODR &= ~(GPIO_Pin_15); //turn off PB15
break;
case Temp_ADC8:
GPIOD->ODR |= GPIO_Pin_8; //turn on PD8
GPIOB->ODR |= GPIO_Pin_15; //turn on PB15
GPIOD->ODR &= ~(GPIO_Pin_9);
break;
case Water_ADC:
break;
default:
break;
}
}
Motor Speed Control Firmware
The motor speed control firmware controls motor speed by outputting a PWM signal to electronic speed controllers (ESC).
I completed my first four-month co-op with AXYS Technologies in January 2019 and got rehired for an eight-month co-op in September 2020. AXYS Technologies is a marine engineering company headquartered in Sidney, BC, and builds autonomous moorings such as the one shown on the right. These moorings collect data for environmental research or renewable energy applications. I was a member of the product development team during these 12 months.
First Co-op at AXYS (January-May 2019)
My first co-op with AXYS was a large collection of small tasks with two medium-sized projects thrown in. Some highlight small tasks would be:
Writing equipment setup work instructions for the manufacturing team
Updating the power management and control system hardware in test system to latest design
Installing, testing, and troubleshooting a wind turbine for power generation on the autonomous moorings
Tested and implemented a new camera onboard moorings
The first of the two medium-sized projects was to create a dashboard for the AXYS test system using Python. The test system is a collection of equipment that aims to simulate a mooring in the field. This dashboard was a web GUI that displayed mooring data in the form of interactive plots and tables. I found this challenging at the time because I had minimal experience with Python programming. However, as a result of this project, I became confident with using Python along with the pandas, matplotlib, and plotly libraries.
The second medium-sized project was to automate the system setup for the industrial PCs (IPCs) onboard AXYS Technologies' flagship floating LiDAR (FLiDAR) buoys. The IPCs were Linux systems that served as the central point of data collection and communication. Before automation, the setup of these systems would take a technician several hours. Through this process, I got proficient in shell scripting and learned a great amount about Linux systems. This project sparked an interest in Linux that I still have to this day.
Second Co-op at AXYS (September-December 2020)
My second co-op with AXYS started with me teaming up with another AXYS Engineer to troubleshoot a thermal bird tracker for one of AXYS's clients. The images taken by the thermal camera were heavily corrupted and our job was to figure out why. We eventually figured out that the corruption was caused by noise in the pulses that were sent to the camera to initiate image capture. This was a fun project where I got to debug a real-world system using an oscilloscope, function generator, and logic analyzer while also regularly reporting progress to the clients.
After the bird tracker project, my tasks were mostly C firmware programming. The remainder of my second co-op with AXYS was spent doing many small bug fixes while I got familiar with the AXYS firmware code. The AXYS firmware is a huge codebase and learning my way around it was easily the greatest challenge I faced in my 12 months as AXYS.
Third Co-op at AXYS: the Buoy-Buoy Link Project (January-April 2021)
During my third Co-op with AXYS, I was assigned my first large project: the Buoy-Buoy Link. The goal of this project was to make several buoys/moorings in a system act as one using master-slave based radio communication network. The overall function of this system is shown in the flow chart below:
This was a project I did for AXYS on my own from spec sheet to testing. Although this was a challenging project with many parts where I felt completely lost, it was also a rewarding endeavour, and am happy about the knowledge and experience I gained from its completion.
Key Takeaways from Working at AXYS
Through the 12 months I spent at AXYS, my most valuable takeaway was that I lost my fear of asking for help. There were many times where I was completely lost while working on an assignment that was out of my comfort zone and in those situations, I have never regretted asking for help. At AXYS, I learned the limits of personal perseverance and that the most valuable resource in an engineering team is the knowledge of those around you.
My Co-op With Cellula Robotics
My four month work term with Cellula Robotics was my fifth and final co-op. I feel this co-op is where all the skills I've developed through school, work with AXYS, and work with AUVIC come together. This was a fast paced co-op with the major focus being preparing Solus-LR for the July 2022 AUV Demo. This involved the testing and troubleshooting of subsystems within the Solus-LR AUV to ensure system and crew readiness in addition to updating cabling and wiring diagrams to keep documentation up to date as changes are made. Another task of mine was to manage the microsub launch. This involved performing writing missions for the microsubs, working with the software team to integrate the launch mechanism, and performing the daily microsub predive prior to AUV launch.
Other Projects
In this section, I have projects that I worked on that weren't with a particular organization. These would be things such as school or personal projects.
ECE 299 Design Project: Alarm Clock Design With Arduino, KiCAD, and TinkerCAD
The ECE 299 alarm clock design project was a fun school project and the first time I've done a PCB design. In this project, I designed a functional alarm clock Arduino shield that could display time, set time, set alarms, and snooze. In the end, this alarm clock design got 100%.
Alarm Clock PCB:
Alarm Clock PCB Enclosure (designed to look like a cartoon bomb):
Alarm Clock Arduino Code:
int displays[] = {3, 5, 6, 9, NULL}; //contains all pins connected to the MOSFET
int segments[] = {1, 4, 7, 8, NULL}; //
// segments to be set to display a number
int numbers[][4] = {{NULL}, // 0
{segments[0], NULL}, // 1
{segments[1], NULL}, // 2
{segments[0], segments[1], NULL}, // 3
{segments[2], NULL}, // 4
{segments[2], segments[0], NULL}, // 5
{segments[2], segments[1], NULL}, // 6
{segments[2], segments[1], segments[0], NULL}, // 7
{segments[3], NULL}, // 8
{segments[3], segments[0], NULL}, // 9
NULL};
// segments which must not be set to display a number
int not_numbers[][5] ={{segments[0], segments[1], segments[2], segments[3], NULL}, // 0
{segments[1], segments[2], segments[3], NULL}, // 1
{segments[0], segments[2], segments[3], NULL}, // 2
{segments[2], segments[3], NULL}, // 3
{segments[0], segments[1], segments[3], NULL}, // 4
{segments[1], segments[3], NULL}, // 5
{segments[0], segments[3], NULL}, // 6
{segments[3], NULL}, // 7
{segments[0], segments[1], segments[2], NULL}, // 8
{segments[1], segments[2], NULL}, // 9
NULL};
int time[4] = {2, 3, 5, 9}; //array used for displaying the actual time
int alarmTime[4] = {0, 0, 0, 0};
int brightness = 255; //default brightness
int leds = 10; //the pin the LEDS in between the 7-segment display for signifing a colon are connected to.
int buzzer = 11; //pin the buzzer is connected to
int photo = A0; //pin the phototransistor is connected to.
int currentDisplay = 0;
int stopAlarmButton = 0;
int increaseButton = 0;
int decreaseButton = 0;
int snoozeButton = 2;
int setAlarmButton = 3;
int buttonDelay = 50;
int snoozeInterval = 1;
bool alarmState = false; // boolean for dertermining whether the alarm is active or not.
//id
void setup()
{
pinMode(photo, INPUT); //for the button
pinMode(buzzer, OUTPUT); //output for buzzer activation
int count;
for(count = 0; count < 4; count++){ //statement for declaring the pins the segment array is connected to as outputs
pinMode(segments[count], OUTPUT);
}
for(count = 0; displays[count] != NULL; count++) pinMode(displays[count], OUTPUT);
pinMode(leds, OUTPUT);
// timer based interrupt setup for the display
cli();//stop interrupts
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// set entire TCCR1B register to 0
TCNT1 = 0;//initialize counter value to 0
// sets display switching to 100Hz
// lower = faster display switching
// prescalar value
// timer runs at 16MHz
// OCR1A = 3910; // 3910 ~= (16*10^6)/(1023*4Hz)
OCR1A = 16; // 16 ~= (16*10^6)/(1023*1000Hz)
// turn on CTC mode (capture compare mode)
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS12) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
}
// interrupt runs at ~100Hz
ISR(TIMER1_COMPA_vect){
digitalWrite(displays[currentDisplay], LOW);
currentDisplay = (currentDisplay+1)%5;
displayNumber(time[currentDisplay]);
analogWrite(displays[currentDisplay], brightness);
}
void displayNumber(int num)
{
int count;
// iteratively turns off all segments not involded in displaying num
for(count = 0; not_numbers[num][count] !=NULL; count++){
digitalWrite(not_numbers[num][count], LOW);
}
// iteratively turns on all segments that coresponds to num being displayed
for(count = 0; numbers[num][count] != NULL; count++){
digitalWrite(numbers[num][count], HIGH);
}
}
int button(){ //this function determines which button has been pressed and returns a value related the the button
if(digitalRead(A1) && !digitalRead(A2) && !digitalRead(A3) && !digitalRead(A4)&& !digitalRead(A5)) return 1; //if button 1 (set alarm) is pressed
else if(!digitalRead(A1) && digitalRead(A2) && !digitalRead(A3) && !digitalRead(A4)&& !digitalRead(A5)) return 2; //if button 2 (set time) is pressed
else if(!digitalRead(A1) && !digitalRead(A2) && digitalRead(A3) && !digitalRead(A4)&& !digitalRead(A5)) return 3; //if button 3 (snooze) is pressed
else if(!digitalRead(A1) && !digitalRead(A2) && !digitalRead(A3) && digitalRead(A4)&& !digitalRead(A5)) return 4; //if button 4 (increase) is pressed
else if(!digitalRead(A1) && !digitalRead(A2) && !digitalRead(A3) && !digitalRead(A4)&& digitalRead(A5)) return 5; //if button 5 (decrease) is pressed.
else return 0;
}
void setTime(){
bool set_hours = true;
int display = 1;
int hour = 10*time[0] + time[1];
int minute = 10*time[2] +time[3];
cli(); // stop interrupts
for(int i = 0; displays[i] != NULL; i++) digitalWrite(displays[i], LOW);
while(1){
digitalWrite(displays[display-1], LOW);
if(set_hours){
display = (display%2) + 1; // alternates between 1 and 2
} else{
if(display == 3) display = 4; //alternates between 3 and 4
else display = 3;
}
displayNumber(time[display-1]);
analogWrite(displays[display-1], brightness);
delay(1);
if(button() == 5){
if(set_hours){
hour++;
if(hour >23) hour = 0;
time[0] = hour/10;
time[1] = hour%10;
} else {
minute++;
if(minute >59) minute = 0;
time[2] = minute/10;
time[3] = minute%10;
}
delay(buttonDelay);
} else if(button() == 4){
if(set_hours){
hour--;
if(hour < 0) hour = 23;
time[0] = hour/10;
time[1] = hour%10;
} else{
minute--;
if(minute < 0) minute = 59;
time[2] = minute/10;
time[3] = minute%10;
}
delay(buttonDelay);
} else if (button() == 2) {
for(int i = 0; displays[i] != NULL; i++) digitalWrite(displays[i], LOW);
while(button() == 2);
if(set_hours) set_hours = false;
else break;
delay(buttonDelay);
}
time[0] = hour/10;
time[1] = hour%10;
}
sei(); // start interrupts
}
void setAlarm(){ //function for setting the alarm
bool set_hours = true;
int display = 1;
int hour = 10*alarmTime[0] + alarmTime[1];
int minute = 10*alarmTime[2] +alarmTime[3];
alarmState = true;
cli(); // stop interrupts
for(int i = 0; displays[i] != NULL; i++) digitalWrite(displays[i], LOW);
while(1){
digitalWrite(displays[display-1], LOW);
if(set_hours){
display = (display%2) + 1; // alternates between 1 and 2
} else{
if(display == 3) display = 4; //alternates between 3 and 4
else display = 3;
}
displayNumber(alarmTime[display-1]);
analogWrite(displays[display-1], brightness);
delay(1);
if(button() == 5){ //if button for increasing the digit on the LED is pressed
if(set_hours){
hour++;
if(hour >23) hour = 0;
alarmTime[0] = hour/10;
alarmTime[1] = hour%10;
} else {
minute++;
if(minute >59) minute = 0;
alarmTime[2] = minute/10;
alarmTime[3] = minute%10;
}
delay(buttonDelay);
} else if(button() == 4){ //if button for decreasing digit on LED is pressed.
if(set_hours){
hour--;
if(hour < 0) hour = 23;
alarmTime[0] = hour/10;
alarmTime[1] = hour%10;
} else{
minute--;
if(minute < 0) minute = 59;
alarmTime[2] = minute/10;
alarmTime[3] = minute%10;
}
delay(buttonDelay);
} else if (button() == 1) {
for(int i = 0; displays[i] != NULL; i++) digitalWrite(displays[i], LOW);
while(button() == 1);
if(set_hours) set_hours = false;
else break;
delay(buttonDelay);
}
alarmTime[0] = hour/10;
alarmTime[1] = hour%10;
}
sei(); // start interrupts
}
unsigned long previousMillis = 0;
void timeFunction() //this function controls the time
{
unsigned long currentMillis = millis(); //these three lines constantly check whether a second has gone by.
if(currentMillis - previousMillis >= 1000)
{
previousMillis = currentMillis; //restart the minute counter
time[3]++; //increase LED 1 by 1 minute
if(time[3] > 9)
{
time[3] = 0; //set LED 1 back to zero
time[2]++; //increase LED 2 by one.
if( time[2] > 5) //if LED 2 is greater than 59 minutes.
{
time[2] = 0; //reset LED 2 back to 0.
time[1]++; //increase LED 3 by 1.
if (time[1] > 9) //if LED 3 is greater than 9
{
time[1] = 0; //set LED 3 back to 0
time[0]++; //increase LED 4 to 1.
}
}
}
}
if(time[0] == 2 && time[1] == 4) //if the clock goes over 23:59, reset all LED's to 0
{
time[3] = 0;
time[2]= 0;
time[1] = 0;
time[0] = 0;
}
}
void buttonCheck(){
switch(button()){
case 1: //case if the set alarm button was pressed.
while(button() == 1);
setAlarm(); //go to set alarm function
delay(buttonDelay);
break;
case 2: //case if the set time button was pressed.
while(button() == 2);
setTime(); //go to set time function
delay(buttonDelay);
break;
case 3: //case if the snooze button was pressed while the alarm is not sounding.
// toggles the alarm
while(button() == 3);
alarmState = !alarmState;
if(alarmState == true){
unsigned long prev = millis();
// plays a tone to indicate the alarm is active
while(millis()-prev < 5)
tone(buzzer, 1000);
noTone(buzzer);
}
break;
case 4:
break;
case 5:
break;
default:
break;
}
}
void alarm(){ //function for activating the alarm
if(time[0] == alarmTime[0] && //if the time array is equal to the set alarmTime array, activate the buzzer
time[1] == alarmTime[1] &&
time[2] == alarmTime[2] &&
time[3]== alarmTime[3] && alarmState) {
while(button() == 0){
tone(buzzer, 300);
}
noTone(buzzer);
if(button() == 3){ // if snooze is pressed
int minutes = 10*alarmTime[2] + alarmTime[3];
int hours = 10*alarmTime[0] + alarmTime[1];
minutes += snoozeInterval;
if(minutes > 59){
minutes = minutes -59;
hours++;
if(hours > 23) hours = 0;
}
alarmTime[0] = hours/10;
alarmTime[1] = hours%10;
alarmTime[2] = minutes/10;
alarmTime[3] = minutes%10;
alarmState = true;
while(button() == 3);
} else alarmState = false;
}
}
void updateBrightness(){
// update brightness variable with the value read at analog input 0
// if V >= 4.83V (4.83/5)*1023 = ~988
if(analogRead(photo) > 800){
brightness = 255 - (analogRead(photo)/5);
} else if((analogRead(photo) < 800) && (analogRead(photo) > 700)){
brightness = 255 - (analogRead(photo)/8);
} else {
brightness = 255;
}
// if 4.7V
}
void loop(){
analogWrite(leds, brightness);
buttonCheck();
updateBrightness();
alarm();
timeFunction();
}
Personal Soldering Fume Extractor Design Using Solidworks and 3D Printing
The soldering fume extractor was a personal project I did mostly to learn Solidworks and 3D printing. To make use of a 120V AC PC fan that was given to me by a friend, I got a metal tool mount from the dollar store for $5. The next step was to find a way to securely mount the fan and this is where the Solidworks design came in. This project provided me with a good introduction to basic Solidworks modelling and 3D printing. The results are shown in the images below: