Merge pull request 'Finished ross content, added skill matrix, li, and ul, and improved many existing components, created and refactored to unified layouts and grid, visual improvements with proper column to row collapsing' (#13) from website-content-updates into main
All checks were successful
Build and Test - Production / test (push) Successful in 5m3s
Build and Test - Production / build_and_push (push) Successful in 5m1s
Build and Test - Production / deploy_production (push) Successful in 3s

Reviewed-on: #13
This commit was merged in pull request #13.
This commit is contained in:
2025-12-06 18:07:53 +00:00
29 changed files with 766 additions and 358 deletions

View File

@@ -1,6 +1,9 @@
ADCP
Altium
ASSEM
astrojs
Atmel
barebones
Candian
caperren
CEOAS
@@ -10,6 +13,7 @@ CONSERV
Corwin
dangerousthings
Dechorionator
ebox
fhhs
flowbite
HDFS
@@ -19,10 +23,13 @@ hwupload
iceops
ITAR
Jetson
KFSK
leconte
Loctite
luxon
MGMT
Mokai
Multimeters
nixos
offroad
Onshape
@@ -30,6 +37,7 @@ OSSM
OSURC
Perren
Perren's
Pixhawk
pubpath
RFID
RSSI

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -2,4 +2,4 @@
---
<h1 class="text-xl font-extrabold md:text-3xl"><slot /></h1>
<h1 class="text-xl font-extrabold sm:text-2xl md:text-3xl"><slot /></h1>

View File

@@ -2,4 +2,4 @@
---
<h2 class="my-4 font-bold md:text-2xl"><slot /></h2>
<h2 class="text-lg font-bold sm:text-xl md:text-2xl"><slot /></h2>

View File

@@ -2,4 +2,4 @@
---
<h3 class="mt-4 mb-2 font-bold md:text-lg"><slot /></h3>
<h3 class="text-md font-semibold sm:text-lg md:text-xl"><slot /></h3>

9
src/components/Li.astro Normal file
View File

@@ -0,0 +1,9 @@
---
---
<>
<li>
<slot />
</li>
</>

View File

@@ -9,10 +9,12 @@ interface Props {
const { title, href, target = "_blank" } = Astro.props;
---
<a
class="text-caperren-green border-caperren-green hover:border-caperren-green-light hover:text-caperren-green-light rounded-2xl border-2 bg-black p-2"
href={href}
target={target}
>
{title}
</a>
<div class="mx-auto">
<a
class="text-caperren-green border-caperren-green hover:border-caperren-green-light hover:text-caperren-green-light rounded-2xl border-2 bg-black p-2"
href={href}
target={target}
>
{title}
</a>
</div>

View File

@@ -39,7 +39,7 @@ import { Image } from "astro:assets";
</svg>
</button>
<div
class="z-40 mt-1 hidden w-full lg:block lg:w-auto"
class="z-40 mt-6 hidden w-full lg:mt-0 lg:block lg:w-auto"
id="navbar-multi-level"
>
<NestedNavbarEntry items={siteLayout} />

View File

@@ -19,29 +19,34 @@ const { pathname } = Astro.url;
---
<ul
class={"flex flex-col p-4 bg-black border-caperren-green " +
(depth ? "space-y-2" : "items-start lg:flex-row lg:space-x-8 lg:mt-0 ")}
class:list={[
"border-caperren-green flex flex-col space-y-4 space-x-8 bg-black",
depth
? "space-y-4 py-4"
: "items-start lg:mt-0 lg:flex-row lg:space-y-0 lg:space-x-8",
]}
>
{
items.map(
(entry) =>
(entry.enabled ?? true) && (
<li>
<li class="">
{Array.isArray(entry.children) && entry.children.length ? (
<div
class={
pathname.startsWith(getHrefPath(entry))
? "border-caperren-green border-b-2"
: ""
}
>
<div>
<button
id={"dropdownNavbarLink" + getNavLinkSuffix(entry)}
data-dropdown-toggle={
"dropdownNavbar" + getNavLinkSuffix(entry)
}
data-dropdown-placement="bottom"
class="hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between px-3 py-2 lg:p-0 lg:hover:bg-transparent"
data-dropdown-placement="bottom-start"
data-dropdown-offset-distance="5"
data-dropdown-offset-skidding="12"
class:list={[
"hover:text-caperren-green-light lg:hover:text-caperren-green-light flex w-full items-center justify-between lg:p-0 lg:hover:bg-transparent",
pathname.startsWith(getHrefPath(entry))
? "border-caperren-green border-b-2"
: false,
]}
>
{entry.navText}
<svg
@@ -61,7 +66,7 @@ const { pathname } = Astro.url;
</button>
<div
id={"dropdownNavbar" + getNavLinkSuffix(entry)}
class="border-caperren-green z-10 hidden w-screen border bg-black shadow-sm lg:w-max"
class="border-caperren-green z-10 hidden w-max max-w-screen border bg-black px-6 shadow-sm"
>
<Astro.self
items={entry.children}
@@ -71,17 +76,16 @@ const { pathname } = Astro.url;
</div>
</div>
) : (
<div
class={
pathname === getHrefPath(entry)
? "border-caperren-green border-b-2"
: ""
}
>
<div>
<a
href={getHrefPath(entry)}
target={getHrefPath(entry).startsWith("/") ? "" : "_blank"}
class="hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent px-3 py-2 lg:p-0"
class:list={[
"hover:text-caperren-green-light ring-caperren-green-dark block bg-transparent lg:p-0",
pathname === getHrefPath(entry)
? "border-caperren-green border-b-2"
: false,
]}
aria-current={
pathname === getHrefPath(entry) ? "page" : undefined
}

36
src/components/Ol.astro Normal file
View File

@@ -0,0 +1,36 @@
---
import Li from "@components/Li.astro";
import type { ComponentPropsBase } from "@interfaces/components.ts";
import type { lineItem } from "@interfaces/ul-li.ts";
interface Props extends ComponentPropsBase {
lineItems?: lineItem[];
depth?: number;
}
const { class: className, lineItems, depth = 0 } = Astro.props;
---
<ol
class:list={["list-inside list-disc", className, depth > 0 ? "ps-3" : false]}
>
{
lineItems ? (
lineItems.map((line) => (
<Li>
{line.item}
{line.subItems ? (
<Astro.self
class={className}
lineItems={line.subItems}
depth={depth + 1}
/>
) : undefined}
</Li>
))
) : (
<slot />
)
}
</ol>

View File

@@ -0,0 +1,21 @@
---
const hasHeader = Astro.slots.has("header");
const hasDefault = Astro.slots.has("default");
---
<div class="grid grid-cols-1 gap-3">
{
Astro.slots.has("header") && (
<div>
<slot name="header" />
</div>
)
}
{
Astro.slots.has("default") && (
<div class="grid grid-cols-1 gap-3">
<slot />
</div>
)
}
</div>

View File

@@ -1,7 +1,13 @@
---
import type { ComponentPropsBase } from "@interfaces/components.ts";
interface Props extends ComponentPropsBase {
initialTab?: boolean;
}
const { class: className, initialTab = true } = Astro.props;
---
---
<div class="">
<slot />
<div class:list={className}>
{initialTab && <>&ensp;</>}<slot />
</div>

View File

@@ -2,6 +2,6 @@
---
<div class="space-y-2">
<div class="grid grid-cols-1 gap-2">
<slot />
</div>

View File

@@ -7,6 +7,10 @@ const keys: { [key: string]: string } = {
ADCP: "Acoustic doppler current profiler",
COTS: "Consumer off-the-shelf",
CTD: "Conductivity, temperature, and depth sensor",
GUI: "Graphical user interface",
NUC: "A small and low-power computer made by Intel",
PCBs: "Printed circuit boards",
UPS: "Uninterruptible power supply",
};
const key: string | undefined = Astro.props.key;

View File

@@ -0,0 +1,30 @@
---
import H3 from "@components/H3.astro";
import PageGroup from "@components/PageGroup.astro";
import Ul from "@components/Ul.astro";
import type { categorySkills } from "@interfaces/skill-matrix.ts";
interface Props {
categorizedSkills: categorySkills[];
}
const { categorizedSkills } = Astro.props;
---
<PageGroup>
<Fragment slot="header"><H3>Relevant Skills</H3></Fragment>
<div
class="border-caperren-green grid grid-flow-row gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
>
{
categorizedSkills.map((categorySkills) => (
<div>
<div class="text-sm font-extrabold">{categorySkills.category}</div>
<hr class="text-caperren-green" />
<Ul class="text-sm" lineItems={categorySkills.skills} />
</div>
))
}
</div>
</PageGroup>

36
src/components/Ul.astro Normal file
View File

@@ -0,0 +1,36 @@
---
import Li from "@components/Li.astro";
import type { ComponentPropsBase } from "@interfaces/components.ts";
import type { lineItem } from "@interfaces/ul-li.ts";
interface Props extends ComponentPropsBase {
lineItems?: lineItem[];
depth?: number;
}
const { class: className, lineItems, depth = 0 } = Astro.props;
---
<ul
class:list={["list-inside list-disc", className, depth > 0 ? "ps-3" : false]}
>
{
lineItems ? (
lineItems.map((line) => (
<Li>
{line.item}
{line.subItems ? (
<Astro.self
class={className}
lineItems={line.subItems}
depth={depth + 1}
/>
) : undefined}
</Li>
))
) : (
<slot />
)
}
</ul>

View File

@@ -4,7 +4,7 @@ export const siteLayout: navLink[] = [
{ navText: "About", path: "" },
{ navText: "Education", path: "education" },
{
navText: "Experiences",
navText: "Experience",
path: "experience",
children: [
{
@@ -29,7 +29,6 @@ export const siteLayout: navLink[] = [
path: "osu-ceoas-ocean-mixing-group",
children: [
{
enabled: false,
navText: "Robotics Oceanographic Surface Sampler",
path: "robotic-oceanographic-surface-sampler",
},

View File

@@ -0,0 +1,6 @@
import type { lineItem } from "@interfaces/ul-li.ts";
export interface categorySkills {
category: string;
skills: lineItem[];
}

4
src/interfaces/ul-li.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface lineItem {
item: string;
subItems?: lineItem[];
}

View File

@@ -4,6 +4,7 @@ import "@styles/global.css";
import Footer from "@components/Footer.astro";
import H1 from "@components/H1.astro";
import Navbar from "@components/Navbar.astro";
import PageGroup from "@components/PageGroup.astro";
import { pathToMetadata } from "@data/site-layout.ts";
@@ -39,28 +40,32 @@ const pageEnabled = pathToMetadata(Astro.url.pathname).enabled ?? true;
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{pageEnabled ? pageTitle : "Corwin Perren"}</title>
</head>
<body class="flex h-dvh w-full max-w-full flex-col bg-black text-white">
<body
class="grid h-dvh w-full max-w-full grid-cols-1 gap-0 bg-black text-white"
>
<div
id="content-body-scrolling"
class="grow overflow-x-hidden overflow-y-scroll"
>
<Navbar />
<main class="mx-6 my-6">
<div class="mb-2 md:mb-6">
{
title && showTitle && pageEnabled && (
<H1 bottomMargin={!subTitles}>{title}</H1>
)
}
{
showTitle &&
pageEnabled &&
subTitles?.map((subTitle) => (
<p class="text-sm font-bold md:text-xl">{subTitle}</p>
))
}
</div>
<div>
<main class="mx-6 my-2">
{
showTitle && pageEnabled && (
<PageGroup>
<Fragment slot="header">
<div class="leading-3">
<H1>{title}</H1>
{subTitles?.map((subTitle) => (
<p class="md:text-md text-sm font-bold italic">
{subTitle}
</p>
))}
</div>
</Fragment>
</PageGroup>
)
}
<div class="grid grid-cols-1 gap-4">
{pageEnabled ? <slot /> : <H1>Under Construction</H1>}
</div>
</main>

View File

@@ -1,10 +1,11 @@
---
import PdfViewer from "@components/Media/PdfViewer.astro";
import BaseLayout from "@layouts/BaseLayout.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
---
<BaseLayout {...Astro.props}>
<div class="h-dvh">
<PdfViewer class="mx-auto" pdf={Astro.props.resume} />
<div class="mt-2 h-dvh">
<PdfViewer pdf={Astro.props.resume} />
</div>
</BaseLayout>

View File

@@ -5,6 +5,7 @@ import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Table from "@components/Table.astro";
import Timeline from "@components/Timeline/Timeline.astro";
@@ -108,16 +109,24 @@ const courseTable: tableData = {
<BaseLayout title="Education">
<Carousel carouselGroup={diplomaCarouselGroup} />
<H2>Timeline</H2>
<Timeline timeline={timeline} />
<H2>Oregon State University</H2>
<H3>
<InlineLink
class="font-bold md:text-lg"
href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework"
>Coursework Archives</InlineLink
>
</H3>
<H3>Course Listing</H3>
<Table data={courseTable} />
<PageGroup>
<Fragment slot="header"><H2>Timeline</H2></Fragment>
<Timeline timeline={timeline} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Oregon State University</H2></Fragment>
<PageGroup>
<Fragment slot="header">
<InlineLink
class="font-bold md:text-lg"
href="https://github.com/caperren/school_archives/tree/master/OSU%20Coursework"
><H3>Coursework Archives</H3></InlineLink
>
</Fragment>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>Course Listing</H3></Fragment>
<Table data={courseTable} />
</PageGroup>
</PageGroup>
</BaseLayout>

View File

@@ -3,8 +3,13 @@ import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import Video from "@components/Media/Video.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -56,83 +61,89 @@ const headerCarouselGroup: carouselGroup = {
],
};
import InlineLink from "@components/InlineLink.astro";
import Video from "@components/Media/Video.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import { subTitles } from "./osu-ceoas-ocean-mixing-group.ts";
---
<ExperienceLayout title="LeConte Glacier Deployments" subTitles={subTitles}>
<Carousel carouselGroup={headerCarouselGroup} />
<H2>Summary</H2>
<H3>Timeline</H3>
<Timeline timeline={deploymentTimeline} />
<H3>Location</H3>
<iframe
class="w-full"
width="600"
height="450"
src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed"
></iframe>
<H2>Details</H2>
<Paragraphs>
<Paragraph>
As part of my time working on the
<InlineLink
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>
Robotic Oceanographic Surface Sampler</InlineLink
>, I had the fantastic opportunity to be deployed at the LeConte Glacier
in Alaska! This started in early 2017 with setup and ocean trials in
nearby Petersburg. The team had sent multiple shipping containers with our
robotic platforms and most equipment to assemble, test, and debug them a
few months prior, allowing us to get to work the moment we arrived. We
spent multiple weeks at the docks with our makeshift workstations built
from plywood and pelican cases, validating the hardware we'd sent, and
making adjustments with improved hardware we'd hand-carried on our
flights. This also provided a great opportunity to work out any final
firmware and/or software bugs while the vehicles were still relatively
easy to retrieve. After a short trip back home to recover, and prep any
last minute items we'd forgotten, our research team flew back and headed
for the glacier!
</Paragraph>
<Paragraph>
The towering mountain of ice sits roughly 30 miles from Petersburg, so
we'd commissioned an off-season fishing vessel, Steller, and it's crew, to
take us as close to it as was reasonably safe. The team worked 24 hours a
day, on two shifts, deploying and retrieving the ROSS platforms,
performing repairs (as needed), recovering/processing collected data,
manually deploying the ship's <PopoverWordDefinition key="CTD" />, and
occasionally spending considerable time pushing icebergs the size of
houses away from an <PopoverWordDefinition key="ADCP" /> mounted to Steller
using fiberglass poles. Many hardware failures had to be solved during these
long days, and it was a very rewarding and creative experience to work around
the limitations of this isolated (and salty) environment.
</Paragraph>
<Paragraph>
On top of being a unique engineering and team building experience, LeConte
lives among the most beautiful places I've yet to experience in this life.
There's something special about being somewhere so incredibly remote and
untouched by humans. The pristine evergreen forests, eerie blue-green hues
of the glacier and icebergs, ancient towering mountains, and genuinely
curious looks from local land and marine life unfamiliar with human
presence made it humbly clear that for once we as humans were the odd ones
out. These trips were ones that I will treasure and think back on fondly
on for the rest of my life.
</Paragraph>
</Paragraphs>
<H2>Videos</H2>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
{
videos.map((video) => (
<Video
videoPath={video}
autoPlay={true}
loop={true}
playsInline={true}
/>
))
}
</div>
<PageGroup>
<Fragment slot="header"><H2>Summary</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>Timeline</H3></Fragment>
<Timeline timeline={deploymentTimeline} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>Location</H3></Fragment>
<iframe
class="w-full"
width="600"
height="450"
src="https://maps.google.com/maps?q=leconte%20glacier&t=k&z=11&ie=UTF8&iwloc=B&output=embed"
></iframe>
</PageGroup>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Details</H2></Fragment>
<Paragraphs>
<Paragraph>
As part of my time working on the
<InlineLink
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>
Robotic Oceanographic Surface Sampler</InlineLink
>, I had the fantastic opportunity to be deployed at the LeConte Glacier
in Alaska! This started in early 2017 with setup and ocean trials in
nearby Petersburg. The team had sent multiple shipping containers with
our robotic platforms and most equipment to assemble, test, and debug
them a few months prior, allowing us to get to work the moment we
arrived. We spent multiple weeks at the docks with our makeshift
workstations built from plywood and pelican cases, validating the
hardware we'd sent, and making adjustments with improved hardware we'd
hand-carried on our flights. This also provided a great opportunity to
work out any final firmware and/or software bugs while the vehicles were
still relatively easy to retrieve. After a short trip back home to
recover, and prep any last minute items we'd forgotten, our research
team flew back and headed for the glacier!
</Paragraph>
<Paragraph>
The towering mountain of ice sits roughly 30 miles from Petersburg, so
we'd commissioned an off-season fishing vessel, Steller, and it's crew,
to take us as close to it as was reasonably safe. The team worked 24
hours a day, on two shifts, deploying and retrieving the ROSS platforms,
performing repairs (as needed), recovering/processing collected data,
manually deploying the ship's <PopoverWordDefinition key="CTD" />, and
occasionally spending considerable time pushing icebergs the size of
houses away from an <PopoverWordDefinition key="ADCP" /> mounted to Steller
using fiberglass poles. Many hardware failures had to be solved during these
long days, and it was a very rewarding and creative experience to work around
the limitations of this isolated (and salty) environment.
</Paragraph>
<Paragraph>
On top of being a unique engineering and team building experience,
LeConte lives among the most beautiful places I've yet to experience in
this life. There's something special about being somewhere so incredibly
remote and untouched by humans. The pristine evergreen forests, eerie
blue-green hues of the glacier and icebergs, ancient towering mountains,
and genuinely curious looks from local land and marine life unfamiliar
with human presence made it humbly clear that for once we as humans were
the odd ones out. These trips were ones that I will treasure and think
back on fondly on for the rest of my life.
</Paragraph>
</Paragraphs>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Videos</H2></Fragment>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
{
videos.map((video) => (
<Video
videoPath={video}
autoPlay={true}
loop={true}
playsInline={true}
/>
))
}
</div>
</PageGroup>
</ExperienceLayout>

View File

@@ -26,4 +26,5 @@ export const deploymentTimeline: timelineEntry[] = [
export const subTitles = [
"Oregon State University",
"College of Earth, Ocean, and Atmospheric Sciences",
"Ocean Mixing Group",
];

View File

@@ -3,17 +3,26 @@ import ExperienceLayout from "@layouts/ExperienceLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Li from "@components/Li.astro";
import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PdfViewer from "@components/Media/PdfViewer.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import PopoverWordDefinition from "@components/PopoverWordDefinition.astro";
import SkillMatrix from "@components/SkillMatrix/SkillMatrix.astro";
import Timeline from "@components/Timeline/Timeline.astro";
import Ul from "@components/Ul.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
import type { categorySkills } from "@interfaces/skill-matrix.ts";
import type { timelineEntry } from "@interfaces/timeline.ts";
import electronics_box from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/electronics-box.jpg";
import jet_drive from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/jet-drive.jpg";
import ross_ebox_4p0 from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-ebox-4p0.jpg";
import ross_on_vessel_at_night from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel-at-night.jpg";
import ross_on_vessel from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-on-vessel.jpg";
import publication from "@assets/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler/ross-publication.pdf";
@@ -31,6 +40,7 @@ const headerCarouselGroup: carouselGroup = {
ross_team,
ross_on_vessel,
ross_on_vessel_at_night,
ross_ebox_4p0,
electronics_box,
jet_drive,
ui,
@@ -50,6 +60,72 @@ const timeline: timelineEntry[] = [
date: "May 2018",
},
];
const categorizedSkills: categorySkills[] = [
{
category: "Electrical",
skills: [
{
item: "Schematic & PCB Design",
subItems: [{ item: "Altium Designer" }],
},
{
item: "PCB Assembly & Rework",
subItems: [
{ item: "Handheld Soldering" },
{ item: "Handheld Hot-Air Reflow" },
{ item: "Oven Reflow" },
],
},
{
item: "Electrical Diagnostics",
subItems: [
{ item: "Multimeters" },
{ item: "Oscilloscopes" },
{ item: "Logic Analyzers" },
],
},
{
item: "Harnessing Fabrication",
subItems: [
{ item: "DC Power & Signal" },
{ item: "Low Frequency RF (<1GHz)" },
{ item: "Waterproofing" },
],
},
{
item: "Simulation",
subItems: [{ item: "LTspice" }],
},
],
},
{
category: "Software & Environments",
skills: [
{ item: "Git" },
{
item: "Programming Languages",
subItems: [
{ item: "Python 2/3" },
{ item: "Bash Shell Scripting" },
{ item: "Low-Level Embedded C/C++ (Atmel Studio)" },
{ item: "High-Level Embedded C/C++ (Arduino/Teensy)" },
{ item: "Matlab" },
],
},
{
item: "Operating Systems",
subItems: [
{
item: "Linux",
subItems: [{ item: "Ubuntu" }, { item: "Raspbian" }],
},
{ item: "Microsoft Windows" },
],
},
],
},
];
---
<ExperienceLayout
@@ -57,56 +133,158 @@ const timeline: timelineEntry[] = [
subTitles={subTitles}
>
<Carousel carouselGroup={headerCarouselGroup} />
<div class="mt-4 flex items-center justify-center">
<div class="grid grid-flow-row place-content-center gap-4 md:grid-flow-col">
<LinkButton
href="https://tos.org/oceanography/article/autonomous-ctd-profiling-from-the-robotic-oceanographic-surface-sampler"
title="Official Scientific Publication"
/>
<LinkButton
href="https://www.kfsk.org/2017/04/19/remote-controlled-kayaks-ready-research-leconte-glacier/"
title="KFSK Petersburg Feature / Interview"
/>
</div>
<H2>Summary</H2>
<H3>Timeline</H3>
<Timeline timeline={timeline} />
<H3>Key Takeaways</H3>
<ul class="list-inside list-disc">
<li>
<div class="inline-block">
Assembled, fabricated, and debugged both custom and
<PopoverWordDefinition key="COTS" />
hardware and electronics.
</div>
</li>
<li>Two</li>
<li>Three</li>
</ul>
<h3 class="my-4 font-bold md:text-lg">Skills Used</h3>
<div
class="border-caperren-green relative grid grid-flow-row gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
>
<div>
<div class="text-sm font-extrabold">Software</div>
<hr class="text-caperren-green" />
<ul class="list-inside list-disc text-sm">
<li>One</li>
<li>Two</li>
</ul>
<PageGroup>
<Fragment slot="header"><H2>Summary</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>Timeline</H3></Fragment>
<Timeline timeline={timeline} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>Key Takeaways</H3></Fragment>
<Ul>
<Li
>Hand assembled and validated dozens of custom <PopoverWordDefinition
key="PCBs"
/>, wiring harnesses, and electronics boxes</Li
>
<Li
>Wrote, debugged, and assisted with development of embedded firmware</Li
>
<Li
>Accompanied the team on two deployments to the LeConte glacier in
Alaska to gather ice-water melt and mixing data</Li
>
</Ul>
</PageGroup>
<SkillMatrix categorizedSkills={categorizedSkills} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Details</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>ROSS Overview</H3></Fragment>
<Paragraphs>
<Paragraph>
ROSS was a gasoline-powered water-sampling robotics platform built
around a Mokai jet-drive kayak. It's purpose was to continuously, and
often autonomously, gather water data over extremely long distances
and/or in locations where human-safety concerns would make gathering
it manually too risky. There were a variety of sensors it could be
outfitted with depending on the needs of the exact research project
and destination, but some common ones were an <PopoverWordDefinition
key="ADCP"
/> for gathering 3D water current vector data, a <PopoverWordDefinition
key="CTD"
/> for measuring water conductivity/temperature/depth, and a high-precision
GPS for generating meaningful 3D plots of the sensor data. These kayaks
have been deployed to places like the Indian/Pacific Ocean mixing line,
and along the active LeConte glacier terminus in Alaska, gathering novel
data on how vastly different bodies of water act when mixing.
</Paragraph>
<Paragraph>
In its original configuration, the Mokai kayak's throttle and steering
were already drive-by-wire, which made it an excellent starting point
for automating. It was also designed for easy transport, breaking down
into three major compartments that could easily fit in the back of a
short-bed pickup. For our custom hulls, Mokai also thickened the
plastic significantly and provided a bare minimum of electronics. This
barebones platform was then modified by our team to include a
storm-surge-rated intake and exhaust for the engine, a keel to improve
rough sea stability, a large alternator, and a plethora of mount
points the electronics, batteries, fuel, sensors, and radios.
</Paragraph>
<Paragraph>
In terms of the electronics and software for this project, the kayak
itself was centered around a Pixhawk flight controller flashed with a
modified Rover variant (this was before a dedicated boat option
existed). One pelican-case electronics box housed this controller, a
small <PopoverWordDefinition key="NUC" /> with <PopoverWordDefinition
key="UPS"
/>, wifi router, radio control receiver, satellite modem, and quite a
few custom <PopoverWordDefinition key="PCBs" /> for interfacing with external
electronics and implementing glue logic/safety overrides. A second box housed
nothing but sealed lead-acid batteries, which were charged by the alternator
on later revisions of the platform. The PC ran a custom python script, which
interfaced with a Matlab GUI over a remote radio link. The kayak could also
be overridden with an FrSky RC controller, when at close range, and additionally
allowed for direct control without the PC needing to be in-the-loop. To
see some of the custom hardware inside of these boxes, check out Nick McComb's
design pages for them
<InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards#omg"
>here</InlineLink
>! For even more context on ROSS, and history from before I joined the
project, check out his <InlineLink
href="https://nickmccomb.net/college/ross">summary page</InlineLink
>.
</Paragraph>
</Paragraphs>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>My Experience</H3></Fragment>
<Paragraphs>
<Paragraph>
I first started on this project by doing what I thought was a one-off
help session for Nick, working on an issue he was having getting
ROSS's engine to start and shut down properly. I had more experience
with engines, and engine control, so I quickly realized that a beefier
and high-voltage-rated relay was needed to avoid arc-welding the
contacts closed during shutdown. He rolled out a <InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/ross-ebox-auxillary"
>new board revision</InlineLink
> with those changes and it was the final version used for the rest of ROSS's
lifetime. This little taste of the project, and some wishful prodding from
Nick, was enough for me to join the team part-time.
</Paragraph>
<Paragraph>
While the original plan for me was to re-write the <PopoverWordDefinition
key="GUI"
/> for ROSS in Python using Qt, it turns out they needed my help on the
electrical and firmware side more than anything, so most of my time at the
lab was focused on that. I hand-assembled so many of Nick's circuit boards
at this lab that I still can pick his out of a lot from design aesthetic
alone! I also helped with plenty of wiring harness builds, electrical box
fabrication, embedded firmware development, and of course, plenty of electrical
and software debugging. One thing that this project taught me very quickly
was how difficult it was to make reliable hardware in a high vibration,
electrically noisy, and salt-laden environments. The number of PCBs we went
through, alongside wiring harnesses, was pretty incredible considering the
lengths we went to in order to protect them.
</Paragraph>
<Paragraph>
A very unique aspect of this team/project, and a large part of why I
was drawn to it, was that it was about as hands-on as you could
possibly get. Doubly so for an undergraduate student! Not only did I
get to design and repair a real robot, but it was actually being used
for proper scientific research! We would regularly go to Newport, OR
for testing and have to make crazy additions and repairs on the fly.
This got even more extreme during my deployments to the <InlineLink
href="http://localhost:4321/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>LeConte glacier</InlineLink
>, as you had to get creative and fix things with what you had on-hand
due to how remote we were. These are experiences that graduate
students rarely even get to have, so I'm extremely thankful and fond
of the time I spent here. Huge shout out to <InlineLink
href="https://nickmccomb.net">Nick</InlineLink
>, again, who made it possible in the first place! Also be sure to
check out the scientific paper on this project below!
</Paragraph>
</Paragraphs>
</PageGroup>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Official Scientific Publication</H2></Fragment>
<div class="h-334">
<PdfViewer pdf={publication} />
</div>
<div>
<div class="text-sm font-extrabold">Electrical</div>
<hr class="text-caperren-green" />
</div>
<div>
<div class="text-sm font-extrabold">Mechanical</div>
<hr class="text-caperren-green" />
</div>
<div>
<div class="text-sm font-extrabold">Other</div>
<hr class="text-caperren-green" />
</div>
</div>
<H2>Details</H2>
power and voltage logging
<H2>Official Scientific Publication</H2>
<div class="h-334">
<PdfViewer pdf={publication} />
</div>
</PageGroup>
</ExperienceLayout>

View File

@@ -5,6 +5,7 @@ import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
@@ -27,42 +28,49 @@ const rfidImplantCarouselGroup: carouselGroup = {
---
<HobbyLayout title="Body Mods">
<H2>RFID Implant</H2>
<Carousel carouselGroup={rfidImplantCarouselGroup} />
<H3>Details</H3>
<Paragraphs>
<Paragraph>
Back when I was in college, a few of my friends and I got this crazy idea
to all get RFID implants together. They are essentially the same things
you'd use to microchip a pet, but with a slightly different firmware
configuration, allowing scans with any 125KHz-compatible reader. The
implants came from <InlineLink
href="https://dangerousthings.com/product/xem/"
>dangerousthings.com</InlineLink
>, and we were lucky enough to have a vet-med student as a friend who made
the installation a quick and painless process! I'm glad that I'm not
afraid of needles, as the 16 gauge injector the kit came with was nothing
to scoff at. Since healing, you would never know the implant was there,
with the site leaving no scar or visible indication of its presence.
</Paragraph>
<Paragraph>
With that out of the way, our group began work on hardware which would
support the new implants. The goal was to have a generic usb-keyboard
emulator for typing passwords with a valid scan, a car off-acc-on ignition
replacement, and a fairly specialized modification to the OSU Robotics
Club's doorway scanning system so they would support these on top of the
official OSU ID cards. As tends to happen, life got busy, and only the
usb-keyboard emulator actually came to fruition. The electronics and
primary firmware were handled by <InlineLink href="https://nickmccomb.net"
>Nick McComb</InlineLink
>, enclosure by <InlineLink href="https://dylanthrush.com"
>Dylan Thrush</InlineLink
>, and I supported some minor firmware development and debugging. If you
want to see an example of the keyboard emulator unlocking a PC, check out
the video on <InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module"
>Nick's website</InlineLink
>!
</Paragraph>
</Paragraphs>
<PageGroup>
<Fragment slot="header"><H2>RFID Implant</H2></Fragment>
<Carousel carouselGroup={rfidImplantCarouselGroup} />
<PageGroup>
<Fragment slot="header"><H3>Details</H3></Fragment>
<Paragraphs>
<Paragraph>
Back when I was in college, a few of my friends and I got this crazy
idea to all get RFID implants together. They are essentially the same
things you'd use to microchip a pet, but with a slightly different
firmware configuration, allowing scans with any 125KHz-compatible
reader. The implants came from <InlineLink
href="https://dangerousthings.com/product/xem/"
>dangerousthings.com</InlineLink
>, and we were lucky enough to have a vet-med student as a friend who
made the installation a quick and painless process! I'm glad that I'm
not afraid of needles, as the 16 gauge injector the kit came with was
nothing to scoff at. Since healing, you would never know the implant
was there, with the site leaving no scar or visible indication of its
presence.
</Paragraph>
<Paragraph>
With that out of the way, our group began work on hardware which would
support the new implants. The goal was to have a generic usb-keyboard
emulator for typing passwords with a valid scan, a car off-acc-on
ignition replacement, and a fairly specialized modification to the OSU
Robotics Club's doorway scanning system so they would support these on
top of the official OSU ID cards. As tends to happen, life got busy,
and only the usb-keyboard emulator actually came to fruition. The
electronics and primary firmware were handled by <InlineLink
href="https://nickmccomb.net">Nick McComb</InlineLink
>, enclosure by <InlineLink href="https://dylanthrush.com"
>Dylan Thrush</InlineLink
>, and I supported some minor firmware development and debugging. If
you want to see an example of the keyboard emulator unlocking a PC,
check out the video on <InlineLink
href="https://nickmccomb.net/college/printed-circuit-boards/computer-access-module"
>Nick's website</InlineLink
>!
</Paragraph>
</Paragraphs>
</PageGroup>
</PageGroup>
</HobbyLayout>

View File

@@ -1,7 +1,12 @@
---
import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro";
import LinkButton from "@components/LinkButton.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import HobbyLayout from "@layouts/HobbyLayout.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -31,44 +36,53 @@ const headerCarouselGroup: carouselGroup = {
};
---
<HobbyLayout title="Motorcycling - Chubby Buttons 2 Mount">
<HobbyLayout
title="Chubby Buttons 2 Mount"
subTitles={["Hobbies", "Motorcycling"]}
>
<Carousel carouselGroup={headerCarouselGroup} />
<div class="mt-4 flex items-center justify-center">
<div class="flex items-center justify-center">
<LinkButton
href="https://cad.onshape.com/documents/816b0b1bef7883d4dc25c66c/v/e11fe68753e080b72015cfb8/e/3802abbd9d7b7c4d2c7ebad3"
title="Onshape CAD Design Files"
/>
</div>
<p class="mt-4">
Having ridden motorcycles since I was sixteen, and being an avid music
enjoyer, I'd been looking for a way to improve my music listening experience
while on-the-go. One large pain-point I'd always had was with controlling
track selection and volume levels while my gloves were on, as smartphones
don't respond very well to this, if at all. In 2023 I found out about chubby
buttons, a low-power and highly water-resistant media controller
specifically designed for use with gloves! The only problem was that it was
designed to be worn on your arm using a strap, which isn't very practical on
a motorcycle.
</p>
<p class="mt-4">
When starting this project, I'd recently gotten a 3D Printer, so having some
baseline modelling skills I took some measurements, and began designing a
proper mount. I already owned and used many 1" RAM compatible mounts and
gear on my bikes, so I decided to make this one natively support the ball
size to use an existing clamp I had stored away. This design was the first
where I decided to use heat-set inserts in the plastic, along with some
medium-strength Loctite on the fasteners, due to the high-vibration
environment the mount would see. The print was also done using a UV
resistant, high-temp rated, and non-water-absorbing ASA filament, as the
direct expose to the elements would not allow something like cheap PLA to
last very long.
</p>
<p class="mt-4">
While my first iteration was sized appropriately and went together with no
issues, the ball mount neck ended up snapping due to a low infill
percentage. After changing that area to 100% infill, including a handful of
the rear mount layers that it attached to, a second iteration has worked
perfectly for a few years now! If you're interested in printing this
yourself, feel free to download the model using the button under the photos!
</p>
<PageGroup>
<Fragment slot="header"><H2>Details</H2></Fragment>
<Paragraphs>
<Paragraph>
Having ridden motorcycles since I was sixteen, and being an avid music
enjoyer, I'd been looking for a way to improve my music listening
experience while riding. One large pain-point I'd always had was
controlling track selection and volume levels while my gloves were on,
as smartphones don't respond very well to this, if at all. In 2023 I
found out about chubby buttons, a low-power and highly water-resistant
media controller specifically designed for use with gloves! The only
problem was that it was designed to be worn on your arm using a strap,
which isn't very practical on a motorcycle.
</Paragraph>
<Paragraph>
When starting this project, I'd recently gotten a 3D printer, so having
some baseline modelling skills I took some measurements, and began
designing a proper mount. I already owned and used many 1" RAM
compatible mounts and gear on my bikes, so I decided to make this one
natively support the ball size to use an existing clamp I had stored
away. This design was the first where I decided to use heat-set inserts
in the plastic, along with some medium-strength Loctite on the
fasteners, due to the high-vibration environment the mount would see.
The print was also done using a UV resistant, high-temp rated, and
non-water-absorbing ASA filament, as the direct exposure to the elements
would not allow something like cheap PLA to last very long.
</Paragraph>
<Paragraph>
While my first iteration was sized appropriately and went together with
no issues, the ball mount neck ended up snapping due to a low infill
percentage. After changing that area to 100% infill, including a handful
of the layers at the rear mounting face where the neck attaches, a
second iteration has worked perfectly for a few years now! If you're
interested in printing this yourself, feel free to download the model
using the button under the photos!
</Paragraph>
</Paragraphs>
</PageGroup>
</HobbyLayout>

View File

@@ -4,6 +4,7 @@ import HobbyLayout from "@layouts/HobbyLayout.astro";
import H2 from "@components/H2.astro";
import H3 from "@components/H3.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import type { carouselGroup } from "@interfaces/image-carousel.ts";
@@ -39,26 +40,35 @@ const kz750CarouselGroup: carouselGroup = {
};
---
<HobbyLayout title="Motorcycling - Lineup">
<H2>Current Lineup</H2>
<H3>2015 Yamaha FJR 1300</H3>
<Carousel carouselGroup={fjrCarouselGroup} />
<H3>2021 CSC SG400</H3>
<Carousel carouselGroup={cscCarouselGroup} />
<H2>Prior Lineup</H2>
<H3>2005 Suzuki DRZ 400</H3>
<Carousel carouselGroup={drzCarouselGroup} />
<h3 class="my-4 font-bold underline md:text-lg">
1991 Kawasaki Concours ZG1000
</h3>
<Carousel carouselGroup={concoursCarouselGroup} />
<H3>1979 Kawasaki KZ750</H3>
<Carousel carouselGroup={kz750CarouselGroup} />
<H3>1991 Kawasaki Ninja 600R</H3>
<Carousel carouselGroup={ninjaCarouselGroup} />
<HobbyLayout title="Lineup" subTitles={["Hobbies", "Motorcycling"]}>
<PageGroup>
<Fragment slot="header"><H2>Current Lineup</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>2015 Yamaha FJR 1300</H3></Fragment>
<Carousel carouselGroup={fjrCarouselGroup} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>2021 CSC SG400</H3></Fragment>
<Carousel carouselGroup={cscCarouselGroup} />
</PageGroup>
</PageGroup>
<PageGroup>
<Fragment slot="header"><H2>Prior Lineup</H2></Fragment>
<PageGroup>
<Fragment slot="header"><H3>2005 Suzuki DRZ 400</H3></Fragment>
<Carousel carouselGroup={drzCarouselGroup} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>1991 Kawasaki Concours ZG1000</H3></Fragment>
<Carousel carouselGroup={concoursCarouselGroup} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>1979 Kawasaki KZ750</H3></Fragment>
<Carousel carouselGroup={kz750CarouselGroup} />
</PageGroup>
<PageGroup>
<Fragment slot="header"><H3>1991 Kawasaki Ninja 600R</H3></Fragment>
<Carousel carouselGroup={ninjaCarouselGroup} />
</PageGroup>
</PageGroup>
</HobbyLayout>

View File

@@ -4,6 +4,7 @@ import BaseLayout from "@layouts/BaseLayout.astro";
import H2 from "@components/H2.astro";
import InlineLink from "@components/InlineLink.astro";
import Carousel from "@components/Media/CustomCarousel/CustomCarousel.astro";
import PageGroup from "@components/PageGroup.astro";
import Paragraph from "@components/Paragraph.astro";
import Paragraphs from "@components/Paragraphs.astro";
@@ -21,76 +22,81 @@ const headerCarouselGroup: carouselGroup = {
<BaseLayout title="About" showTitle={false}>
<Carousel carouselGroup={headerCarouselGroup} />
<H2>Who Am I</H2>
<Paragraphs>
<Paragraph>
My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <InlineLink
href="/education">degree in computer science</InlineLink
> from Oregon State University. For as long as I can remember, I've been fascinated
by how things work, never being shy about taking them apart to learn the gritty
details. At a young age, I began tinkering, adding lights and fans and doorbells
to the pretend cardboard box houses my brother and I would play in. Later, I
learned to solder, work on vehicles and engines, install and run Linux, manage
enterprise computing infrastructure, build and repair computers, write scripts,
and by the end of high school set out with a clear goal for my college years.
I wanted to learn and teach myself enough to be able to think up almost any
project, encompassing all facets of engineering, and be capable of driving it
to completion with my own skill set.
</Paragraph>
<Paragraph>
I think young me would be very pleased by how well I managed to achieve
that goal! Through college, I learned electronics and PCB design, embedded
and pc programming, basic mechanical design and fabrication, on top of
learning how to work well with others in a team. I quickly realized that
robotics was an ideal focus due to its inherent multi-disciplinary nature,
and joined the OSU Robotics Club, which introduced me to people who are
still my best friends today. Through student engineering jobs, I had the
unique opportunity to work on some incredible projects such as the
<InlineLink
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>robotic oceanographic surface sampler</InlineLink
>
and an <InlineLink
href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate"
>embryo pick-and-plate machine</InlineLink
>. One my my proudest moments was when our club's mars rover took first
place at the Candian International Rover Challenge in 2018, for which I
was the <InlineLink
href="/experience/osu-robotics-club/mars-rover-software-team-lead"
>software lead</InlineLink
>!
</Paragraph>
<Paragraph>
After a short three-month <InlineLink
href="/experience/spacex/avionics-test-engineering-internship"
>internship</InlineLink
> at SpaceX in Hawthorne at the end of college, I applied for a <InlineLink
href="/experience/spacex/hardware-test-engineer-i-ii"
>test engineering</InlineLink
> position with the company's Starlink team and was hired in mid-2019. For six
years, I developed test system hardware, software, harnesses, mechanical fixtures,
devops infrastructure, websites, and tooling to ensure that Starlink, Falcon,
Dragon, and Starship component tests were producing well-validated and reliable
hardware. Through it all, I got to apply and hone every skill I had developed,
while learning countless more. Now though, it's on to the next adventure, whatever
that may be!
</Paragraph>
<Paragraph>
To learn more about my experiences, hobbies, interests, and skills, feel
free to explore the site! While the short summary above provides some
insight into who I am, it leaves out plenty! For example, I've been an
avid <InlineLink href="/hobby/motorcycling/lineup"
>motorcycle rider</InlineLink
> since I was sixteen, and have an <InlineLink href="/hobby/body-mods"
>rfid implant</InlineLink
> in my hand!
</Paragraph>
<Paragraph>
If you're interested in contacting me, feel free to message on <InlineLink
href="https://github.com/caperren">LinkedIn</InlineLink
>, or via the primary contact methods on my <InlineLink
href="/resume/2025-11-10-infrastructure-engineer">resume</InlineLink
>.
</Paragraph>
</Paragraphs>
<PageGroup>
<Fragment slot="header"><H2>Who Am I</H2></Fragment>
<Paragraphs>
<Paragraph>
My name is Corwin Perren, and I'm a multi-disciplinary engineer with a <InlineLink
href="/education">degree in computer science</InlineLink
> from Oregon State University. For as long as I can remember, I've been fascinated
by how things work, never being shy about taking them apart to learn the gritty
details. At a young age, I began tinkering, adding lights and fans and doorbells
to the pretend cardboard box houses my brother and I would play in. Later,
I learned to solder, work on vehicles and engines, install and run Linux,
manage enterprise computing infrastructure, build and repair computers, write
scripts, and by the end of high school set out with a clear goal for my college
years. I wanted to learn and teach myself enough to be able to think up almost
any project, encompassing all facets of engineering, and be capable of driving
it to completion with my own skill set.
</Paragraph>
<Paragraph>
I think young me would be very pleased by how well I managed to achieve
that goal! Through college, I learned electronics and PCB design,
embedded and pc programming, basic mechanical design and fabrication, on
top of learning how to work well with others in a team. I quickly
realized that robotics was an ideal focus due to its inherent
multi-disciplinary nature, and joined the OSU Robotics Club, which
introduced me to people who are still my best friends today. Through
student engineering jobs, I had the unique opportunity to work on some
incredible projects such as the
<InlineLink
href="/experience/osu-ceoas-ocean-mixing-group/robotic-oceanographic-surface-sampler"
>robotic oceanographic surface sampler</InlineLink
>
and an <InlineLink
href="/experience/osu-sinnhuber-aquatic-research-laboratory/zebrafish-embryo-pick-and-plate"
>embryo pick-and-plate machine</InlineLink
>. One my my proudest moments was when our club's mars rover took first
place at the Candian International Rover Challenge in 2018, for which I
was the <InlineLink
href="/experience/osu-robotics-club/mars-rover-software-team-lead"
>software lead</InlineLink
>!
</Paragraph>
<Paragraph>
After a short three-month <InlineLink
href="/experience/spacex/avionics-test-engineering-internship"
>internship</InlineLink
> at SpaceX in Hawthorne at the end of college, I applied for a <InlineLink
href="/experience/spacex/hardware-test-engineer-i-ii"
>test engineering</InlineLink
> position with the company's Starlink team and was hired in mid-2019. For
six years, I developed test system hardware, software, harnesses, mechanical
fixtures, devops infrastructure, websites, and tooling to ensure that Starlink,
Falcon, Dragon, and Starship component tests were producing well-validated
and reliable hardware. Through it all, I got to apply and hone every skill
I had developed, while learning countless more. Now though, it's on to the
next adventure, whatever that may be!
</Paragraph>
<Paragraph>
To learn more about my experiences, hobbies, interests, and skills, feel
free to explore the site! While the short summary above provides some
insight into who I am, it leaves out plenty! For example, I've been an
avid <InlineLink href="/hobby/motorcycling/lineup"
>motorcycle rider</InlineLink
> since I was sixteen, and have an <InlineLink href="/hobby/body-mods"
>rfid implant</InlineLink
> in my hand!
</Paragraph>
<Paragraph class="mt-8 flex flex-col items-center" initialTab={false}>
<div>
If you're interested in contacting me, feel free to message on <InlineLink
href="https://github.com/caperren">LinkedIn</InlineLink
>, or via the primary contact methods on my <InlineLink
href="/resume/2025-11-10-infrastructure-engineer">resume</InlineLink
>.
</div>
</Paragraph>
</Paragraphs>
</PageGroup>
</BaseLayout>