Other Projects

Other projects I am curently working on








Header section illustration

My Self-Hosted AI Stacks


To maintain full control over my data and customize my AI tools for specific use cases, I’ve built and currently maintain two distinct self-hosted AI stacks—each tailored for a different domain of functionality.


Text-Based AI Stack with Ollama

The first stack is dedicated to natural language processing and text-based interactions. At its core, it runs Ollama, paired with Open WebUI, a lightweight web-based user interface. This setup acts as a privacy-focused alternative to commercial platforms like ChatGPT, Gemini, or Claude. Rather than relying on third-party servers, all inference and processing happens locally on my own hardware. This provides greater transparency, data control, and reliability—especially important when working with sensitive information.


This stack also gives me the flexibility to swap in or fine-tune specific models that excel at particular tasks. For instance, I often run models optimized for programming help, data analysis, or documentation generation, depending on what I’m working on. This modularity means I can use the right tool for the job without being locked into a single vendor’s ecosystem or limitations.



Multimedia AI Stack for Image and Voice

The second AI stack is focused on multimodal generation, specifically AI-generated imagery and text-to-speech voice synthesis. This stack is purpose-built for creative and interactive applications—whether that’s generating illustrations for projects, creating synthetic voices for personal assistants, or experimenting with character-driven content.


In the near future, I plan to extend this stack’s capabilities even further. Upcoming features include local AI-based network traffic monitoring, which will allow for intelligent pattern recognition and anomaly detection within my home network. I also intend to integrate this stack more deeply with Home Assistant, enabling smarter, more adaptive responses to different inputs.

OrangePi Zero 3 as USB Mass Storage Gadget


This guide will walk you through how to configure a OrangePi Zero 3 (henceforth known as OPZ3) as a mass storage device, and then have that virtual mass storage device availble via SMB (samba). My use case for this was to have it connected to my Elegoo Saturn Mono Resin Printer so I could add and remove print files without having to bother with a physical USB drive. If this is also your use case, feel free to follow this guide to a T. Otherwise this guide will also work for you, but there may need to be some changes you make along the way. Also if you are using this for the same printer as me and have a FDM 3D printer, feel free to print the two below files to attach the OPZ3 to the printer.


Plate Top

Materials Needed

Orange Pi Zero 3 (or whatever OrangePi device you plan on using.)

A way to power the device. You cannot use the USB C port just for power, as this port is also needed to emulate the mass storage. Refer to the next section "Powering the OPZ3" for suggested options.

A micro SD card. Ideally something 16GB or larger. Faster speeds will help with some load times when performing setup, but in the long run it won't make a huge difference.

A way to connect that micro SD card to a computer.

USB C to USB A cable capable of transfering data. (Or whatever USB cable you will need to fir your project, just ensure it is capable of sending data.)

I would always suggest putting a device like this in some form of case. Either order one or 3D print one yourself (like the ones linked above)



Powering the OPZ3

First thing we need to do is power the OPZ3. Unfortunatly the USB C port on the board is the port we HAVE to use to emulate a mass storage device, so just plugging in a powered USB C cable won't work. The USB A port is only compatable in host usb mode, so we can't use that either (only works with keyboards, mice, etc). Therefore there are two ways we can go about powering the OPZ3. The first option is through a USB C adaptor like this. The second way is through the GPIO pins. The OPZ3 takes 5V power in, so if you have a reliable 5V source of power, you can connect it to the associated pins. If you are following along to use this with a Elegoo Saturn, you are in luck, as we can use the 5V pins on the motherboard of the Saturn to power our OPZ3. (Just note that turning off the printer will also turn off the OPZ3). You will need 4 jumper cables to do this, I used the ones found here. Carefully analyze the the diagram and pictures below, and wire the OPZ3 the same way. The Rx and Tx cables are supposed to be switch around on the OPZ3. If you have already printed the sideplate, remember to run the cables through the hole in the side.


Diagram 1 Diagram 2

Once you have it wired up, I would suggest powering the OPZ3 to ensure it is getting adequate power before moving onto the next step.



Creating the build enviroment

The next step we have to do is compile our own firmware/Linux Image for the OPZ3. Unfortunatly, the official OS provided by OrangePi does not have the USB Mass Storage drivers enabled, and there is no way to enable them without compiling it ourselves. If you are also using a OrangePi Zero 3, you can download and use the image I have already compiled (skip to the Flashing & Configuring the OS section). If you aren't using that device, or want to compile your own firmware, then continue to follow the guide. I promise it's not that hard, besides I've done all the hard work and researching for you.


OrangePi Zero 3 Image

In order to make our own image, we need to build enviroment. The tools and scripts provided by OrangePi only support Ubuntu 22, so that is what we are going to use. I would reccommend spinning this up in a virtual machine, the desktop or server (minimal) versions both work fine. Once you are logged in, run the following commands:


- git clone https://github.com/orangepi-xunlong/orangepi-build.git -b next Download the tools and script from OrangePi's official Github

- sudo nano /orangepi-build/kernel/orange-pi-6.1-sun50iw9/arch/arm64/boot/dts/allwinner/sun50i-h618-orangepi-zero3.dts In this file, search for dr_mode = "peripheral" and change it to dr_mode = "otg". Note that this specific file is only for the OPZ3. If you are following this guide for a different OrangePi device, look for the appropriate .dts file in the same directory. Use "ctrl+x" to close out of the file, and press "y" to save.

- cd ../../../../../../../ Take us back to the top level directory

- sudo ./build.sh Run the build script


A menu screen should appear. Because we want to build an entire OS, select the following options:

Full OS Image for flashing

Show a kernel configuration menu before compilation

Orangepizero3 (or whatever board you are using)

Use the latest kernel

jammy Ubuntu jammy 22.04 LTS (you don't have to use this, if you want to use a differet version of Ubuntu or Debian, I assume they would work just as well, but I have not tested them. I would not suggest the Raspberry Pi option.)

no Image with console interface (server) (unless you have a screen connected to the OPZ3 and would like a desktop enviroment)

no Standard image with console interface


The script will begin downloading the required packages, this process can take some time on the first run. Let it run untile a firmware configuration menu appears. You are going to want to find and enable the following options. Take note of the "*" and "M" before the option. Navigate the menu by using "Select" to go forward a menu page and "Exit" to go back a menu page. Once all the following options have been selected, "Save" and leave the default file name. Continue to select "Exit' until you leave the menu window.


Device Drivers USB support <M>Inventra Highspeed Dual Role Controller MUSB Mode Selection (X)Dual Role mode


USB Physical Layer drivers <M>NOP USB Transceiver Driver


USB Gadget Support [*] RNDIS


USB Gadget precomposed configurations <M>Mass Storage Gadget


At this point the terminal should be very busy as the kernel is built, configured, and updated. This process took about 45 miutes for me.



Flashing & Configuring the OS

Once the OS is finished building, the .img file will be located in /orangepi-build/output/images/. Flash that .img file to the micro SD card using Balena Etcher or another flashing software. Insert the SD card into the OPZ3 and provide it power. It will automatically start booting.


At this point we need to connect to the OPZ3 over the network. In my case, I was permantly using an wired ethernet connection, however if you plan to use wifi, I would suggest connecting it via ethernet for now and following the next few steps, and you can switch over to wifi at a later point.


Once your device is connected to your network, you will need to find its IP address. You can usually do this inside your router, however that is outside the scope of this document. I would suggest Googling how to if you are unsure. The device is usually named "orangepi"


You now need to SSH into the OPZ3. If you are using mac or linux, the command ssh [email protected] will work, with x.x.x.x being the IP address of the OPZ3. The password is "orangepi". If you are using windows, I would suggest downloading and using an application called PuTTY. Enter the IP address you found earlier as the address you wish to connect to and select OK/Yes if you get a security warning. When prompted on a black terminal window, enter "orangepi" as both your username and password.


Once logged in, I would suggest running the following commands to perform updates and change the default password, however these steps are optional.


sudo apt update && sudo apt upgrade -y

sudo passwd orangepi


Setting up the USB Mass Storage Share

The first step is to decide how large you want the Mass Storage share to be. I would suggest making it no larger than 2/3 the size of the SD card you are using. For my case, I was using a 32GB SD card, and only wanted it to take up half of the space, so I made my share 16GBs. Once you decide how large you want your share to be, convert that number of GBs into MBs using the calculator here. For example, my 16Gbs was 16384MBs.


Run the following commands, replacing the value "#"" with the number of MBs you calculated in the last step.


sudo dd bs=1M if=/dev/zero of=/piusb.bin count=#- Creates our storage file as the specified size. This will take some time to run

sudo mkdosfs /piusb.bin -F 32 -I- Formats the storage file as FAT32.

sudo mkdir /mnt/usb_share- Mounts the storage file so we can access it.

sudo nano /etc/fstab- Open the configuration file

/piusb.bin /mnt/usb_share vfat users,umask=000 0 2- Add the to the end of the file. "ctrl+x" to close and "y" to save.

sudo mount -a- Reloads the fstab file we just modified


Testing

At this point the share should be working, but it's always a good idea to test it first.

If you are powering the OPZ3 from an external power source (a plug connected to the wall), you will need to modify your USB cable to prevent power from being sent into the device you are trying to connect to. If you are powering the OPZ3 from the same device you are connecting to (for example, if you are following along to do this with the Elegoo Saturn Printer), you can safely ignore the following modification.


Add a small piece of electrical tape to the 5v pin (labeled as 1) on the USB A side. Refer to the two pictures below.


Diagram 1 Diagram 2

Finally, plug in one side of the cable to the USB C port of the OPZ3 and the other side to the USB A port on the printer (or whatever device you are trying to connect to).


To have our OPZ3 show up a as a mass storage device, first run

cd /mnt/usb_share

and then

wget https://loganseverance.com/assets/projects/gear.ctb

to download a test .ctb file. Do NOT start a print using this file, as this is configured for my printer, but it will at least show up on your printer so you can verify it is working. You can also download any other test file you would like. Finally run

sync

to sync the file you downloaded to the storage file. Run the command

sudo modprobe g_mass_storage file=/piusb.bin stall=0 ro=1

to make your storage file available.


The printer (or other device) should now be seeing the virtual USB device! Note: If it doesn't show up, go back and ensure you have followed all previous steps, including ensuring that the USB cable is not damaged and is capable of sending and recieving data, and that the device you are connecting to can read the FAT32 format. Another note: during my testing, I was able to get the device to show up on a Ubuntu test machine and the Elegoo Saturn printer, but not my Macbook M1 laptop. This may be a configuration issue, but not that some devices will play better with this setup than others. Was not tried on Windows.


Once that is all working as expected, dismount the device by running

sudo modprobe -r g_mass_storage



Configuring SMB/Samba

This part is pretty easy and doesn't need much explanation. Perform the following steps.

sudo apt install samba winbind -y- Installs Samba

sudo nano /etc/samba/smb.conf - Edit Samba configuration

Add the following to end of the file. "ctrl+x" to exit and "y" to save.

[usb]
browseable = yes
path = /mnt/usb_share
guest ok = yes
read only = no
create mask = 777

sudo systemctl restart smbd.service - Restart samba service with new configuration.


Test out the samba share by navigating to share in your file browser. Will have to search for it via IP. You do not need credentials to access the share, a guest account or unauthenticated access should work.



Installing Watchdog

In order for the printer or other device to detect added or removed files from the samba share, we need to add a service that watches for those changes, and then disconnects and reconnects to the USB device to sync those changes. Run the following:


sudo apt install python3-pip- Installs Pip3 for Python

sudo pip3 install watchdog - Installs Watchdog

cd /usr/local/share - Change directory

sudo wget http://rpf.io/usbzw -O usb_share.py- Downloads a python script to check for changes

sudo chmod +x usb_share.py - Gives the script permissions to run

cd /etc/systemd/system- Change directory again

sudo nano usbshare.service- Makes a new file for a new service.

Add the following to the file. "ctrl+x" to exit and "y" to save.

[Unit]
Description=USB Share Watchdog

[Service]
Type=simple
ExecStart=/usr/local/share/usb_share.py
Restart=always

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload- Reload the service daemon so it can see our new service

sudo systemctl enable usbshare.service - Enable our new service to run at boot

sudo systemctl start usbshare.service- Start our new service now


That's it, you should be done! You can now add files via SMB, and in ~ 30 seconds they should show up on the connected device.


If you plan on using wifi with your OPZ3, now is the time to configure that. I would suggest this guide here.

Cisco Documentation


Getting started with the basics of configuring a Cisco switch.


Getting Started

The first step in configuring a Cisco switch is to connect to the switch via a console cable. This can be using a application such as PuTTY. If you don't know how to establish a console connection to the switch via Putty, a in-depth guide can be found here .


Privileged Execution Mode

Once a console connection has been established, press any key to get started. On the screen, you should see a terminal-like prompt starting with the word "Switch" (or something else if this switch's hostname has been previously assigned). To start entering commands, the console must be in Privileged Execution Mode. To get there, enter the command enable (or "en" for short). If there is a secret password, it will be prompted here. If not, one can be set up later. A indicator that the console is in Privileged Execution Mode is that the switch's name will end in "#" instead of ">".


Once the console is in Privileged Execution Mode, a variety of commands can be used. Some common ones are listed below. You can see all possible commands by typing ?

cd - Change current directory

copy - Copy from one file to another

delete - Delete a file

dir - List files on a filesystem

mkdir - Create new directory

rmdir - Remove existing directory

ping - Send echo messages

show - Show running system information

vtp - Configure global VTP state

configure - Enter configuration mode

clock - Manage the system clock


For the sake of this documentation, the 4 common commands used in Privileged Execution Mode are:

configure terminal (or "conf t" for short) - Enter the Global Configuration Mode

show running-config (or "sh run" for short) - Shows the current running configuration.

write memory (or "wr mem" for short) Saves the current running config to the startup config. Think of it as saving any changes you made.

exit exits out of Privileged Execution Mode


Global Configuration Mode

Once a console connection has been established and it is in Privileged Execution Mode, Global Configuration Mode can be entered. To enter Global Configuration Mode, enter conf t.The name of the switch should have previously ended in a "#" sign, but should now end in "(config)".


Once the console is in Global Configuration Mode, a variety of different commands can be used. Note that Privileged Execution Mode commands cannot be used here. Some common Global Configuration Mode commands are listed below. You can see all possible commands by typing ?.

banner - Define a login banner

ethernet - Ethernet configuration

interface - Select an interface to configure

ip - Global IPV4 configuration subcommands

ipv6 - Global IPv6 configuration commands

ntp - Configure NTP

vlan - Vlan commands

vtp - Configure global VTP state


For the sake of this documentation, the 4 common commands used in Global Execution Mode are:

hostname Used to change the switch's hostname

enable secret Enables or changes the secret password to get into Privileged Execution Mode.

line vty # # Configures remote access rules for SSH and Telnet

exit exits out of Global Configuration Mode. Goes back to Privileged Execution Mode


Changing the hostname

Assigning a hostname is a good first step when configuring a switch. This hostname will be what other devices see the switch as, and is a good way to help organize a network. To change a switch's host name, enter the Global Configuration Mode.

The format for changing the hostname is hostname {new hostname}. For example, if I wanted my switch to be named "CoreSwitch1" I would enter hostname CoreSwitch1


Enabling a secret password

A good second step and a good security practice is to assign a secret password for console access. This password is required before Privileged Execution Mode can be entered. To enter a secret password, enter the Global Configuration Mode.

The format for entering or changing the secret password is enable secret {secret password}. For example, if I wanted the secret password for my switch to be "Password123" (not a very secure password), I would enter enable secret Password123


Enabling Remote Access

Assuming access to the switch will be needed later, but physically connecting via the console isn't a viable option, a remote access protocol will need to be enabled and configured. Two popular options for remote access into Cisco switches are Telnet and SSH. Because SSH is inherintly more secure than Telnet, that is what this guide will cover. To enable SSH access, enter the Global Configuration Mode.

The format to enable SSH access is line vty # #. vty stands for Virtual Teletype and is a virtual port only used for remote access via SSH or Telnet. The pound signs represent the number of active connections allowed for remote access. For example, the command line vty 0 4 will open a total of 5 (0,1,2,3,4) virtual interfaces that can be accessed. Next, we need to enable SSH on these virtual interfaces. This is done with the command transport input ssh. We then need to allow users to log-in locally with the command login local. Finally, we want our SSH passwords to be safely encrypted on the switch. The best type of encryption available for Cisco switches is type 8. This encryption method is used with the command password 8. A list of all encryption types and best practices when using them is available here.

NOTE: This password SHOULD be different than your secret password entered earlier, just for security purposes. Some newer models of Cisco switch will not even allow the passwords to be the same.


Now we need to make a user to use when logging in via SSH. Exit out of editing SSH settings by running the command exit. Then make a new user with the following command: username {username here} password {password here}. For example, if my username was "Admin" and my password was "Password321", I would enter username admin password Password321


SSH is completely configured at this point, however we need to assign a vlan and IP address to that we have something to SSH into. For this, make sure you are in Global Configuration Mode and enter the command interface vlan {#}. Replace the "#" with your vlan number. For example, if I was using vlan 1, I would enter interface vlan 1. This command will automatically create vlan 1 for us. Next we need to assign a IP address to this switch within this vlan. This is done with the command ip address {ip address of switch} {subnet mask of switch}. For example, if I wanted to assign the IP address 192.168.1.10 with the subnet mask 255.255.255.0, I would enter ip address 192.168.1.10 255.255.255.0. Enter the command no shut (short for no shutdown) to turn on the interface. The command is somewhat backwards logic, as instead of saying "turn on this interface" you are saying "do not turn off this interface", therefore the interface is turned on.


Now we need to assign that vlan and IP to a specific port on our switch. To do this, make sure you are in Global Configuration mode and enter interface {interface name}{interface number}. You can see all available interface by entering the command show interface summary while in Privileged Execution Mode. For example, if I wanted to use interface "GigabitEthernet1/0/25" I would enter the command interface GigabitEthernet1/0/25 (this GigabitEthernet can be appreviated to "Gi"). Then enter the command switchport mode access to change this interface into an access port. Then assign it the defined vlan from earlier using the command switchport access vlan {#}. Using the example from earlier with vlan 1, my command would be switchport access vlan 1. A description can also be added to this port for better organization. Use the command description {description here}


You should now be able to access the switch securely via SSH. To confirm this, we can do a couple things. First, exit back to Privileged Execution Mode and run the command show vlan. This will show a list of all interfaces and their assigned vlans. You should see the interface you assigned earlier under the appropriate vlan. If everything looks right, you can either plug your switch into your existing network through the configured interface and attempt to ping it, or you can test locally by connecting a computer to the switch via the configured interface. Change the PC's network interface to a static address in the same vlan as the switch and see if you can ping it. If you can, attempt to SSH into it as well. ssh {username created earilier}@{switch address} If you can successfully SSH into the switch, then this part of the setup is complete.


Configuration of Ports

Now that the switch and managment interface is set up, the remaining ports need to be configured. Similarly to what was done in the previous step, the interfaces need to all be assignned a vlan. Instead of doing it one by one, a range can be defined. Exit to Global Configuration Mode and enter the command interface range {interface range}. For example, if I wanted to select interfaces GigabitEthernet1/0/1 throught GigabitEthernet1/0/24, I would run interface range GigabitEthernet1/0/1-24. After the interface(s) have been selected, commands such as switchport mode access, switchport access vlan#, and description {description here} can be run and will be applied to all selected interfaces.

Let's Connect!