commit a9aba887b084cddc93c276375827b8135a014043 Author: Administrator Date: Wed Nov 12 19:33:48 2025 -0600 init diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/blog/Air-Quality-Monitor.png b/blog/Air-Quality-Monitor.png new file mode 100644 index 0000000..4d4caff Binary files /dev/null and b/blog/Air-Quality-Monitor.png differ diff --git a/blog/Air-Quality-Monitor.svg b/blog/Air-Quality-Monitor.svg new file mode 100644 index 0000000..2dfadf7 --- /dev/null +++ b/blog/Air-Quality-Monitor.svg @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/STEAM-CONTROLLER-2.svg b/blog/STEAM-CONTROLLER-2.svg new file mode 100644 index 0000000..7038f30 --- /dev/null +++ b/blog/STEAM-CONTROLLER-2.svg @@ -0,0 +1,135 @@ + + + + diff --git a/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.png b/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.png new file mode 100644 index 0000000..902f49d Binary files /dev/null and b/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.png differ diff --git a/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.svg b/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.svg new file mode 100644 index 0000000..1346137 --- /dev/null +++ b/blog/STEAM-CONTROLLER-BLACK-ACC-003-814585020069-400889494.svg @@ -0,0 +1,1521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/STEAM-CONTROLLER.svg b/blog/STEAM-CONTROLLER.svg new file mode 100644 index 0000000..474aa9d --- /dev/null +++ b/blog/STEAM-CONTROLLER.svg @@ -0,0 +1,136 @@ + + + + diff --git a/blog/SteamController/index.html b/blog/SteamController/index.html new file mode 100644 index 0000000..a5ad83f --- /dev/null +++ b/blog/SteamController/index.html @@ -0,0 +1,805 @@ + + + + + + Project Article - Tech Blog + + + + + + + +
+ + +
+
04
+ +

Steam Controller Driver for Actual Motors

+

+ lorem ipsum +

+
+
React
+
Node.js
+
WebSocket
+
MongoDB
+
Redis
+
+
+ + + + + +
+

Backstory

+

+ I was cleaning out a closet and I discovered an old Steam Controller I bought about a decade ago. To my surprise, it still turned on. Way to go, Duracell. I remembered it being a pretty niche thing that had very little adoption and was quickly deprecated by Valve. Boy was I wrong. I turned it on and immediately remembered how amazing the haptic feedback was on the touchpads. How could I forget the grip inputs? Even a gyroscope! The design and hardware were top notch, but adoption was the problem. +

+ +

+ The haptic feedback touchpads made Games like Civ and XCOM playable on the couch. But I had my eyes on something much bigger target that could use a controller with fourteen buttons, six directional axes, two analog triggers, and a gryoscope: Flight Simulator. With traditional controllers, you had to have a plethora of different button configurations to make it work. Joysticks make it hard to sense and control your aircraft. Touchpads allow for refined movement with feedback to help you ramp up and down with ease. +

+ +
+
+
+ +

Flight Simulator

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget tortor massa. Sed sed nulla vitae enim accumsan pulvinar fermentum nec nunc. Phasellus scelerisque sem vel massa tempus, id lobortis arcu mattis. Donec sed lectus cursus libero auctor facilisis. Donec mattis luctus congue. Maecenas et nunc odio. Sed eu diam lectus. Phasellus sit amet mi vitae lacus rhoncus elementum. Nullam dictum justo sed dignissim placerat. Pellentesque porta velit vel luctus efficitur. +

+ +

WebSocket Server

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eget tortor massa. Sed sed nulla vitae enim accumsan pulvinar fermentum nec nunc. Phasellus scelerisque sem vel massa tempus, id lobortis arcu mattis. Donec sed lectus cursus libero auctor facilisis. Donec mattis luctus congue. Maecenas et nunc odio. Sed eu diam lectus. Phasellus sit amet mi vitae lacus rhoncus elementum. Nullam dictum justo sed dignissim placerat. Pellentesque porta velit vel luctus efficitur. +

+ +
    +
  • Handles client connections and disconnections gracefully
  • +
  • Broadcasts operations to all connected clients in a room
  • +
  • Implements heartbeat mechanisms to detect stale connections
  • +
  • Scales horizontally using Redis pub/sub for multi-server deployments
  • +
+ +
+
+
+

Physical Applications

+

Parsing the Byte Stream

+

+ If I want to make a driver for this controller, I need to see what the output is. Controllers are just like a keyboard: character devices. When you type an "a" character into your keyboard, the keyboard sends data for an "a" key to the computer. When you press an "a" button on your controller, it does just about the same thing. It sends data, which in this case is 41 in hex or 01000010 in binary, to the computer and it is handled by the OS, which monitors for any special inputs, before sending it to the application you're using. This happens so quickly, we don't even think about it. It's a little more complicated than that, but I'll explain later. What I need to do is reverse engineer what data the inputs on my controller output and write a driver to turn those inputs into actions. +

+

+ Knowing my controller is a character device, I can "read" it just like a file, but I have to jump through a couple hoops first. I don't know what that actual file is yet. I'm going to have to check around. When you plug a device into a computer running an OS, it has to decide what to do with it. Devices that already have their firmware configured will tell the OS what to do with it, usually through the Human Interface Device (HID) standard. So I need to ask the OS where it put the file. +

+
$ lsusb
+Bus 003 Device 022: ID 28de:1102 Valve Software Wired Controller
+

+ This gives us the Bus number, Device number, and both the Device ID and Vendor ID. The bus and device number are abstractions for userspace created by udev. In this instance they seem very redundant and abstract away from what I'm trying to accomplish. What I am trying to do is take the raw input from the device at a kernel level, so I need to ask udev where it's getting this device from. +

$ udevadm info -q path -n /dev/bus/usb/003/022
+/sys/devices/pci0000:00/0000:00:14.0/usb3/3-7
+ This shows us where the kernel has put the device. We can go into this series of directories and find it. Problem solved! Almost. +

+ +

+ In this case, with the steam controller, it actually has 3 subdevices, each with their own hidraw file. As it turns out, the controller can also be used in place of a mouse and keyboard. One could poke around the files some more to differentiate them, but I found a better solution. +

sudo usbhid-dump -m 28de:1102 -ed > descriptor.txt
+grep -A 10 "DESCRIPTOR" descriptor.txt | grep -v "DESCRIPTOR" | grep -v "STREAM" | tr -d ' \n' > descriptor_hex.txt
+rexx rd.rex -d --hex "$(cat descriptor_hex.txt)"
+
=== /dev/hidraw6 ===
+Name:             Valve Software Wired Controller
+Vendor:Product:   0x28de:0x1102
+Bus type:         3
+Physical:         usb-0000:00:14.0-7/input0
+Descriptor size:  65 bytes
+Descriptor (first 32 bytes): 05 01 09 06 95 01 A1 01 05 07 19 E0 29 E7 15 00 25 01 75 01 95 08 81 02 95 01 75 08 81 01 95 05 
+Type hints:       [KEYBOARD] 
+
+=== /dev/hidraw7 ===
+Name:             Valve Software Wired Controller
+Vendor:Product:   0x28de:0x1102
+Bus type:         3
+Physical:         usb-0000:00:14.0-7/input1
+Descriptor size:  52 bytes
+Descriptor (first 32 bytes): 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 
+Type hints:       [MOUSE] 
+
+=== /dev/hidraw8 ===
+Name:             Valve Software Wired Controller
+Vendor:Product:   0x28de:0x1102
+Bus type:         3
+Physical:         usb-0000:00:14.0-7/input2
+Descriptor size:  33 bytes
+Descriptor (first 32 bytes): 06 00 FF 09 01 A1 01 15 00 26 FF 00 75 08 95 40 09 01 81 02 95 40 09 01 91 02 95 40 09 01 B1 02 
+

+ +

+ I found a tool called RDD! HID Report Descriptor Decoder on github by abend0c1. It takes the data from usbhid-dump and performs lookups on all the devices given. It's a very comprehensive tool that gave much more output than I listed. +

+

Operational Transformation

+

+ The core algorithm that makes collaboration possible is operational transformation (OT). When two users + edit different parts of the document, their operations must be transformed relative to each other to + maintain consistency. +

+ +
+ "Operational transformation is the mathematical foundation that allows distributed systems to converge + to the same state despite concurrent modifications." +
+ +

+ Our implementation uses a three-operation model: insert, delete, and retain. Each operation carries + positional information that gets transformed when concurrent operations occur. +

+ +
+
+
+ +

Key Features

+

+ The final implementation includes several sophisticated features that enhance the collaborative experience: +

+ +
    +
  1. Cursor Tracking: Real-time display of where other users are typing
  2. +
  3. Presence Indicators: Shows who's currently viewing the document
  4. +
  5. Conflict-Free Resolution: Automatic handling of concurrent edits
  6. +
  7. Undo/Redo: Full history management that works across collaborative sessions
  8. +
  9. Rich Text Formatting: Support for bold, italic, lists, and more
  10. +
+ +

Performance Optimizations

+

+ To ensure smooth performance even with large documents and many concurrent users, several optimizations + were implemented: +

+ +
    +
  • Operation batching to reduce network overhead
  • +
  • Delta compression for efficient data transmission
  • +
  • Lazy loading of document history
  • +
  • Client-side caching with service workers
  • +
+ +
+
+
+ +

Code Implementation

+

+ The operational transformation logic is implemented in JavaScript. Here's a simplified example of how + operations are transformed when they conflict: +

+ +
function transformOperation(op1, op2) {
+  // If operations affect different positions, no transformation needed
+  if (op1.position < op2.position) {
+    return op1;
+  }
+  
+  // Transform op1 based on op2s changes
+  if (op2.type === 'insert') {
+    return {
+      ...op1,
+      position: op1.position + op2.length
+    };
+  }
+  
+  if (op2.type === 'delete') {
+    return {
+      ...op1,
+      position: Math.max(op1.position - op2.length, op2.position)
+    };
+  }
+  
+  return op1;
+}
+ +

WebSocket Event Handler

+

+ The server-side WebSocket handler manages incoming operations and broadcasts them to all connected clients: +

+ +
io.on('connection', (socket) => {
+  console.log('Client connected:', socket.id);
+  
+  socket.on('join-document', (docId) => {
+    socket.join(docId);
+    socket.to(docId).emit('user-joined', {
+      userId: socket.id,
+      timestamp: Date.now()
+    });
+  });
+  
+  socket.on('operation', (data) => {
+    const { docId, operation, version } = data;
+    
+    // Transform and broadcast operation
+    const transformed = applyTransformation(operation, version);
+    socket.to(docId).emit('operation', transformed);
+    
+    // Save to database
+    saveOperation(docId, transformed);
+  });
+});
+ +

React Component Example

+

+ On the client side, React components manage the editor state and handle user input: +

+ +
const CollaborativeEditor = () => {
+  const [content, setContent] = useState('');
+  const [cursors, setCursors] = useState({});
+  const wsRef = useRef(null);
+  
+  useEffect(() => {
+    // Connect to WebSocket server
+    wsRef.current = io('wss://api.example.com');
+    
+    wsRef.current.on('operation', (op) => {
+      setContent(prev => applyOperation(prev, op));
+    });
+    
+    wsRef.current.on('cursor-move', (data) => {
+      setCursors(prev => ({
+        ...prev,
+        [data.userId]: data.position
+      }));
+    });
+    
+    return () => wsRef.current.disconnect();
+  }, []);
+  
+  return (
+    <div className="editor">
+      <textarea 
+        value={content}
+        onChange={handleChange}
+        onSelect={handleCursorMove}
+      />
+      {renderCursors(cursors)}
+    </div>
+  );
+};
+ +
+
+
+ +

Lessons Learned

+

+ Throughout this project, several important insights emerged about building real-time collaborative systems: +

+ +

+ Network Reliability: Never assume the network is stable. Implementing robust reconnection + logic and conflict resolution for offline edits proved essential for a production-ready system. +

+ +

+ State Management: Keeping client and server state synchronized requires careful attention + to edge cases. The operational transformation algorithm must handle not just simple concurrent edits, but + also complex scenarios involving multiple clients making rapid changes. +

+ +

+ User Experience: The technical implementation, no matter how elegant, means nothing if + users experience lag or data loss. Optimizing for perceived performance was as important as actual performance. +

+ +
+
+
+ +

Future Enhancements

+

+ While the current implementation is robust, several exciting enhancements are planned for future iterations: +

+ +
    +
  • End-to-end encryption for sensitive documents
  • +
  • Voice and video integration for enhanced collaboration
  • +
  • AI-powered suggestions and auto-completion
  • +
  • Mobile app support with native performance
  • +
  • Advanced permission systems for enterprise deployments
  • +
+ +

+ This project demonstrates that building truly collaborative software requires deep understanding of + distributed systems, careful attention to user experience, and thoughtful architecture decisions. The + result is a system that feels magical to users while being built on solid engineering principles. +

+
+
+ + + + + diff --git a/blog/artdeco-article-template.html b/blog/artdeco-article-template.html new file mode 100644 index 0000000..6584410 --- /dev/null +++ b/blog/artdeco-article-template.html @@ -0,0 +1,758 @@ + + + + + + Project Article - Tech Blog + + + + + + + +
+ + +
+
01
+ +

Building a Real-Time Collaborative Editor

+

+ A deep dive into creating a production-ready collaborative text editor using WebSockets, + operational transformation, and modern web technologies. +

+
+
React
+
Node.js
+
WebSocket
+
MongoDB
+
Redis
+
+
+ + + + + +
+

The Challenge

+

+ Building a real-time collaborative editor presents unique technical challenges. When multiple users edit + the same document simultaneously, conflicts arise that must be resolved elegantly without losing data or + disrupting the user experience. This project explores operational transformation algorithms and their + implementation in a modern web application. +

+ +

+ The goal was to create an editor that feels as responsive as Google Docs while maintaining data consistency + across all connected clients. Every keystroke needed to be transmitted, transformed, and applied in real-time + without introducing latency or conflicts. +

+ +
+
+
+ +

Technical Architecture

+

+ The system is built on a microservices architecture with distinct components handling different aspects + of the collaborative experience: +

+ +

WebSocket Server

+

+ At the heart of the system is a Node.js WebSocket server that manages all real-time connections. Each + document session creates a room where clients can join, and all operations are broadcast through this central hub. +

+ +
    +
  • Handles client connections and disconnections gracefully
  • +
  • Broadcasts operations to all connected clients in a room
  • +
  • Implements heartbeat mechanisms to detect stale connections
  • +
  • Scales horizontally using Redis pub/sub for multi-server deployments
  • +
+ +

Operational Transformation

+

+ The core algorithm that makes collaboration possible is operational transformation (OT). When two users + edit different parts of the document, their operations must be transformed relative to each other to + maintain consistency. +

+ +
+ "Operational transformation is the mathematical foundation that allows distributed systems to converge + to the same state despite concurrent modifications." +
+ +

+ Our implementation uses a three-operation model: insert, delete, and retain. Each operation carries + positional information that gets transformed when concurrent operations occur. +

+ +
+
+
+ +

Key Features

+

+ The final implementation includes several sophisticated features that enhance the collaborative experience: +

+ +
    +
  1. Cursor Tracking: Real-time display of where other users are typing
  2. +
  3. Presence Indicators: Shows who's currently viewing the document
  4. +
  5. Conflict-Free Resolution: Automatic handling of concurrent edits
  6. +
  7. Undo/Redo: Full history management that works across collaborative sessions
  8. +
  9. Rich Text Formatting: Support for bold, italic, lists, and more
  10. +
+ +

Performance Optimizations

+

+ To ensure smooth performance even with large documents and many concurrent users, several optimizations + were implemented: +

+ +
    +
  • Operation batching to reduce network overhead
  • +
  • Delta compression for efficient data transmission
  • +
  • Lazy loading of document history
  • +
  • Client-side caching with service workers
  • +
+ +
+
+
+ +

Code Implementation

+

+ The operational transformation logic is implemented in JavaScript. Here's a simplified example of how + operations are transformed when they conflict: +

+ +
function transformOperation(op1, op2) {
+  // If operations affect different positions, no transformation needed
+  if (op1.position < op2.position) {
+    return op1;
+  }
+  
+  // Transform op1 based on op2's changes
+  if (op2.type === 'insert') {
+    return {
+      ...op1,
+      position: op1.position + op2.length
+    };
+  }
+  
+  if (op2.type === 'delete') {
+    return {
+      ...op1,
+      position: Math.max(op1.position - op2.length, op2.position)
+    };
+  }
+  
+  return op1;
+}
+ +

WebSocket Event Handler

+

+ The server-side WebSocket handler manages incoming operations and broadcasts them to all connected clients: +

+ +
io.on('connection', (socket) => {
+  console.log('Client connected:', socket.id);
+  
+  socket.on('join-document', (docId) => {
+    socket.join(docId);
+    socket.to(docId).emit('user-joined', {
+      userId: socket.id,
+      timestamp: Date.now()
+    });
+  });
+  
+  socket.on('operation', (data) => {
+    const { docId, operation, version } = data;
+    
+    // Transform and broadcast operation
+    const transformed = applyTransformation(operation, version);
+    socket.to(docId).emit('operation', transformed);
+    
+    // Save to database
+    saveOperation(docId, transformed);
+  });
+});
+ +

React Component Example

+

+ On the client side, React components manage the editor state and handle user input: +

+ +
const CollaborativeEditor = () => {
+  const [content, setContent] = useState('');
+  const [cursors, setCursors] = useState({});
+  const wsRef = useRef(null);
+  
+  useEffect(() => {
+    // Connect to WebSocket server
+    wsRef.current = io('wss://api.example.com');
+    
+    wsRef.current.on('operation', (op) => {
+      setContent(prev => applyOperation(prev, op));
+    });
+    
+    wsRef.current.on('cursor-move', (data) => {
+      setCursors(prev => ({
+        ...prev,
+        [data.userId]: data.position
+      }));
+    });
+    
+    return () => wsRef.current.disconnect();
+  }, []);
+  
+  return (
+    <div className="editor">
+      <textarea 
+        value={content}
+        onChange={handleChange}
+        onSelect={handleCursorMove}
+      />
+      {renderCursors(cursors)}
+    </div>
+  );
+};
+ +
+
+
+ +

Lessons Learned

+

+ Throughout this project, several important insights emerged about building real-time collaborative systems: +

+ +

+ Network Reliability: Never assume the network is stable. Implementing robust reconnection + logic and conflict resolution for offline edits proved essential for a production-ready system. +

+ +

+ State Management: Keeping client and server state synchronized requires careful attention + to edge cases. The operational transformation algorithm must handle not just simple concurrent edits, but + also complex scenarios involving multiple clients making rapid changes. +

+ +

+ User Experience: The technical implementation, no matter how elegant, means nothing if + users experience lag or data loss. Optimizing for perceived performance was as important as actual performance. +

+ +
+
+
+ +

Future Enhancements

+

+ While the current implementation is robust, several exciting enhancements are planned for future iterations: +

+ +
    +
  • End-to-end encryption for sensitive documents
  • +
  • Voice and video integration for enhanced collaboration
  • +
  • AI-powered suggestions and auto-completion
  • +
  • Mobile app support with native performance
  • +
  • Advanced permission systems for enterprise deployments
  • +
+ +

+ This project demonstrates that building truly collaborative software requires deep understanding of + distributed systems, careful attention to user experience, and thoughtful architecture decisions. The + result is a system that feels magical to users while being built on solid engineering principles. +

+
+
+ + + + + diff --git a/blog/branch-svgrepo-com.svg b/blog/branch-svgrepo-com.svg new file mode 100644 index 0000000..eace000 --- /dev/null +++ b/blog/branch-svgrepo-com.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/blog/circuit-svgrepo-com.svg b/blog/circuit-svgrepo-com.svg new file mode 100644 index 0000000..2b79730 --- /dev/null +++ b/blog/circuit-svgrepo-com.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/cone-svgrepo-com.svg b/blog/cone-svgrepo-com.svg new file mode 100644 index 0000000..100f637 --- /dev/null +++ b/blog/cone-svgrepo-com.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/blog/diode-light-svgrepo-com.svg b/blog/diode-light-svgrepo-com.svg new file mode 100644 index 0000000..75c5775 --- /dev/null +++ b/blog/diode-light-svgrepo-com.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/fist-svgrepo-com.svg b/blog/fist-svgrepo-com.svg new file mode 100644 index 0000000..28bd64b --- /dev/null +++ b/blog/fist-svgrepo-com.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/blog/game-controller-buttons.svg b/blog/game-controller-buttons.svg new file mode 100644 index 0000000..d0ee3c7 --- /dev/null +++ b/blog/game-controller-buttons.svg @@ -0,0 +1,71 @@ + + + + + + + ionicons-v5-g + + + + + + + + + + ionicons-v5-g + + + + diff --git a/blog/game-controller-sharp-svgrepo-com.svg b/blog/game-controller-sharp-svgrepo-com.svg new file mode 100644 index 0000000..2095810 --- /dev/null +++ b/blog/game-controller-sharp-svgrepo-com.svg @@ -0,0 +1,2 @@ + +ionicons-v5-g \ No newline at end of file diff --git a/blog/healthcare-lungs-svgrepo-com.svg b/blog/healthcare-lungs-svgrepo-com.svg new file mode 100644 index 0000000..cd3594d --- /dev/null +++ b/blog/healthcare-lungs-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/blog/homelab.png b/blog/homelab.png new file mode 100644 index 0000000..620f457 --- /dev/null +++ b/blog/homelab.png @@ -0,0 +1,136 @@ + + + + diff --git a/blog/homelab.svg b/blog/homelab.svg new file mode 100644 index 0000000..434738f --- /dev/null +++ b/blog/homelab.svg @@ -0,0 +1,325 @@ + + + + + + + + + + + + diff --git a/blog/index.html b/blog/index.html new file mode 100644 index 0000000..4a60649 --- /dev/null +++ b/blog/index.html @@ -0,0 +1,533 @@ + + + + + + Tech Blog | Projects & Documentation + + + +
+
+
+

BLOG

+

Technical Projects & Innovation

+
+
+ +
+
+
+ +
+
+ + +
+
+
+
+ + + +
+
04
+
+
+ +

Steam Controller Driver For Actual Motors

+

+ Taking a deprecated piece of technology and reverse-engineering the protocol to create custom drivers for games as well as drive two motors over bluetooth using an ESP32. +

+
+
C/C++
+
Hardware
+
+
MongoDB
+
+
+
+
+ + +
+
+
+
+ + + +
+
03
+
+
+ +

Air-Quality Monitoring and Data Collection

+

+ Built an air quality monitoring system using an ESP32 microcontroller that interfaces with three sensors: a BME280 for temperature, humidity, and pressure; an SPS30 for particulate matter (PM2.5/PM10); and an SCD40 for CO2 levels. The ESP32 runs custom firmware that collects sensor data and transmits it over WiFi via HTTP with JSON payloads to an InfluxDB instance running on my homelab. This enables continuous monitoring and historical tracking of indoor air quality metrics. +

+
+
C/C++
+
Soldering
+
I2C
+
InfluxDB
+
+
+
+
+ + +
+
+
+
+ + + +
+
02
+
+
+ +

Home Sweet Homelab

+

+ Welcome to my homelab! Let me show you around. +

+
+
Kubernetes
+
Proxmox
+
ArgoCD
+
OTEL-LGTM Stack
+
+
+
+
+ + +
+
+
+
+ + + +
+
01
+
+
+ +

Automating Automation with n8n

+

+ Architected and implemented a comprehensive CI/CD pipeline for microservices deployment. The sophisticated + system incorporates automated testing, security scanning, containerization, and Kubernetes orchestration, + dramatically reducing deployment time while enhancing reliability through automated rollback capabilities. +

+
+
n8n
+
Kubernetes
+
Terraform
+
AWS
+
+
+
+
+ +
+
+ + + + diff --git a/blog/industrial-robot-factory-svgrepo-com.svg b/blog/industrial-robot-factory-svgrepo-com.svg new file mode 100644 index 0000000..01b7695 --- /dev/null +++ b/blog/industrial-robot-factory-svgrepo-com.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/leaf-svgrepo-com.svg b/blog/leaf-svgrepo-com.svg new file mode 100644 index 0000000..21d361d --- /dev/null +++ b/blog/leaf-svgrepo-com.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/lever-svgrepo-com.svg b/blog/lever-svgrepo-com.svg new file mode 100644 index 0000000..ddceac1 --- /dev/null +++ b/blog/lever-svgrepo-com.svg @@ -0,0 +1,6 @@ + + + +lever + + \ No newline at end of file diff --git a/blog/microchip-svgrepo-com.svg b/blog/microchip-svgrepo-com.svg new file mode 100644 index 0000000..1b2fd73 --- /dev/null +++ b/blog/microchip-svgrepo-com.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/n8n.io.svg b/blog/n8n.io.svg new file mode 100644 index 0000000..062d564 --- /dev/null +++ b/blog/n8n.io.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/n8n.svg b/blog/n8n.svg new file mode 100644 index 0000000..0569e43 --- /dev/null +++ b/blog/n8n.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/network-role-solid-svgrepo-com.svg b/blog/network-role-solid-svgrepo-com.svg new file mode 100644 index 0000000..a467f94 --- /dev/null +++ b/blog/network-role-solid-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + network-role-solid + + + + + + + + + \ No newline at end of file diff --git a/blog/roof-svgrepo-com.svg b/blog/roof-svgrepo-com.svg new file mode 100644 index 0000000..6a8569d --- /dev/null +++ b/blog/roof-svgrepo-com.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/server-database-svgrepo-com.svg b/blog/server-database-svgrepo-com.svg new file mode 100644 index 0000000..4a548f5 --- /dev/null +++ b/blog/server-database-svgrepo-com.svg @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/blog/server-minimalistic-svgrepo-com.svg b/blog/server-minimalistic-svgrepo-com.svg new file mode 100644 index 0000000..16a81a8 --- /dev/null +++ b/blog/server-minimalistic-svgrepo-com.svg @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/server-svgrepo-com.svg b/blog/server-svgrepo-com.svg new file mode 100644 index 0000000..a00f6f4 --- /dev/null +++ b/blog/server-svgrepo-com.svg @@ -0,0 +1,367 @@ + + + + + + + + + + + + diff --git a/blog/smoke-industrial-polution-pollute-svgrepo-com.svg b/blog/smoke-industrial-polution-pollute-svgrepo-com.svg new file mode 100644 index 0000000..cbfec62 --- /dev/null +++ b/blog/smoke-industrial-polution-pollute-svgrepo-com.svg @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.jpg b/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.jpg new file mode 100644 index 0000000..d1175fe Binary files /dev/null and b/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.jpg differ diff --git a/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.svg b/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.svg new file mode 100644 index 0000000..5ffbec1 --- /dev/null +++ b/blog/ss_db2d4943572b38e7687a361b03437ae2c27b4606.1920x1080-1552686322.svg @@ -0,0 +1,883 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blog/steam-svgrepo-com (1).svg b/blog/steam-svgrepo-com (1).svg new file mode 100644 index 0000000..11cbebc --- /dev/null +++ b/blog/steam-svgrepo-com (1).svg @@ -0,0 +1,6 @@ + + + +steam + + \ No newline at end of file diff --git a/blog/steam-svgrepo-com.svg b/blog/steam-svgrepo-com.svg new file mode 100644 index 0000000..3ddecb7 --- /dev/null +++ b/blog/steam-svgrepo-com.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blog/streetlight-svgrepo-com.svg b/blog/streetlight-svgrepo-com.svg new file mode 100644 index 0000000..580cb7f --- /dev/null +++ b/blog/streetlight-svgrepo-com.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/blog/tree-decidious-svgrepo-com.svg b/blog/tree-decidious-svgrepo-com.svg new file mode 100644 index 0000000..259e492 --- /dev/null +++ b/blog/tree-decidious-svgrepo-com.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/blog/tree-of-rectangles-foliage-svgrepo-com.svg b/blog/tree-of-rectangles-foliage-svgrepo-com.svg new file mode 100644 index 0000000..91ef636 --- /dev/null +++ b/blog/tree-of-rectangles-foliage-svgrepo-com.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..30d6c5c --- /dev/null +++ b/index.html @@ -0,0 +1,212 @@ + + + + + + Eric Steen - Resume + + + +
+
+

Eric Steen

+
Site Reliability Engineer / Devops
+
+ 📧 eric@steen.run + 🌍 Grand Forks, ND +
+
+ +
+
+

Professional Summary

+
+

I bring experience from hardware repair to cloud infrastructure. I've worked across the full stack from hardware debugging to kubernetes orchestration. When systems break, I can troubleshoot at any layer.

+
+
+ +
+

Experience

+ +
+

Site Reliability Engineer

+
Core Scientific • Grand Forks • Sep 2024 - Jun 2025
+
    +
  • Set up systems to collect and organize log files from different applications. Grafana Loki Prometheus
  • +
  • Tested new software before it went live, helping other departments connect their tools. Kubernetes Terraform Github Actions
  • +
  • Found new methods of error tracking to detect possible future incidents. Datadog
  • +
  • Fixed internal tools that were notoriously difficult to work with and maintain. Bash Unix Docker
  • +
+
+ +
+

Hardware Engineer

+
Core Scientific • Grand Forks • Feb 2021 - Sep 2024
+
    +
  • Wrote software to automate boring, repetative tasks, which cut downtime by 20%
  • +
  • Led several projects involving dozens of people to upgrade old systems, and did it under budget and ahead of schedule
  • +
  • Successfully scrapped legacy systems in favor of modern and more reliable systems
  • +
  • Record holder for most repairs in a shift, while also doing all the heavy lifting myself.
  • +
+
+
+ +
+

Skills

+
+ Kubernetes + Docker + CI/CD + Trace Repair + Soldering + Circuit Analysis + Embedded Programming + Cloud Services +
+
+ +
+

Projects

+ +
+

Project Name

+
Brief description • Technologies used
+

Description of the project, your role, and the impact or results achieved.

+
+
+
+
+ +