Skip to content

Latest commit

 

History

History
215 lines (186 loc) · 32.7 KB

posts.org

File metadata and controls

215 lines (186 loc) · 32.7 KB

figgyc

Home

Projects

This will (eventually) be an exhaustive list of just about everything somewhat significant I’ve ever done. I think it’s important to be transparent about what you do, it’s helpful to provide a history for people interested in my stuff, and it’s also a cool way to self-reflect on what went wrong and what went right. You can read more about my thought process in this blog article.

UserCSS

One of the best things about the Web is the freedom to customise it as you wish. Userstyles are a nice way to change websites to work how you want them to, not some advertising-backed megacorp. I’ve started writing some userstyles to make my life less dependant on the social media dopamine, and I’m sharing them here to make your life better too.

These files are designed to be used with Stylus or another usercss compatible extension.

Full Focus for YouTube 📦 Install

/images/usercss/distractionfreeyt.png A userstyle to help you avoid wasting time on YouTube. Features:

  • Hides all recommended videos (in the sidebar, the home page, and at the end of videos)
  • Hides Notifications
  • Hides Trending/Explore
  • Hides Autoplay
  • Hides various visual clutter
  • (optional) Hide Subscriptions
  • (optional) Hide the YouTube logo
  • (optional) Enable the Notifications icon to clear notifications without being able to read them (gets rid of the annoying number in the tab titles)

Full Focus for Wolfram Alpha 📦 Install

/images/usercss/fullfocuswa.png A userstyle to remove visual clutter from Wolfram Alpha. Removes basically everything except the search bar and the answers.

Voter.js

Voter.js is a JavaScript website which is a ranking tool, letting a user order things from best to worst using a human-facing sorting system. It is principally designed for competitive word games.

Written in vanilla client-side JavaScript, Voter.js is a simple web application designed primarily for the community of the word game Ten Words of Wisdom and spin-offs (although there is nothing stopping it from being used to rank any list of items). Using a simple yet sophisticated interface combined with an optimised merge sort algorithm, it allows the user to rank a list of responses to a game’s prompt by simply clicking on which of a pair they deem superior. Especially on large sets of responses, voting of which is encouraged by hosts to improve the accuracy of results, it simplifies and accelerates the process since manually sorting a list requires you to retain a rough idea of all the responses and their positions in your memory which can be quite time consuming.

It is widely regarded as a useful tool, generally succeeding the original “voter.exe” project by being cross-platform and including more features such as automatic assembly of the final vote keyword (in TWOWs, responses are mapped to letters and an entire vote can be represented by a list of the characters from best to worst). It has been continuously updated with bug fixes and with additional features, such as keyboard shortcuts, grouping similar responses into tiers to save comparison time, and more.

Bracketcounter

Bracketcounter is a data analysis project, gauging public opinions via YouTube comments.

Using TypeScript and a custom high-performance asynchronous interface to the YouTube API, Bracketcounter counts comments on a YouTube video containing a particular pattern, and presents this in a simple to digest graph format. I designed this to correspond with the video series BFDI which is a viewer voted series in which comments determine the outcome of the game. As a result there is a lot of interest in who is eliminated before the release of the next episode so I decided to fill the niche. In fact this has resulted in a boost of engagement to the original videos as knowing of close results encourages more people to voice their opinion. Additionally, I developed a set of data tools in Python to enable interesting analyses, like the relationships of opinions between two videos. Recently, I’ve made it easier for people creating their own videos to deploy it for their own use, including documentation and adding support for authentication with OAuth to read comments behind moderation filters.

OpenWrt

How to use OpenWrt on the Creality Wifi Box WB-01

A while ago, a friend discovered a cool little gadget on AliExpress called the Creality Box… or is it the Creality Wifi Box? Both of those names are pretty silly to be honest, so I use the model number you can find on the back of the label - the Creality WB-01.

You might have seen the Teaching Tech video, or one of the Hackaday articles discussing this device, and the general gist of it is that it’s a low quality, poorly thought out device. The stock firmware essentially ties your printer to Creality’s cloud servers - that is, if it even decides to work with your printer at all, due to the locked down hardware/software restrictions - without any of the advanced functions of solutions like Octoprint, such as no webcam support, or no ability to just print G-code. Of course, if we can run our own software on it, then we can add extra functionality, so that’s exactly what I set out to do.

This is the first of a series on my adventures into running OpenWrt on this device, the open source firmware that provides a flexible, up to date Linux distribution for many embedded Linux devices. In it I’ll set out the easy way to quickly get up and running with OpenWrt on your Creality box. If you’re more inclined to hear my process and some of the more technical details, check out the OpenWrt tag on my blog and read the rest of the series.

The outcome

With this guide, you’ll be able to install OpenWrt on your Creality WB-01, allowing you to use it for whatever you want, really, without having to worry about the device phoning home. Specifically, I go through setting up 3 applications:

  • Octoprint - the most popular choice for remote controlling 3D printers.
  • Klipper - this setup is more complicated, but in a nutshell, it allows you to take advantage of the “computing power” of the WB-01 to pre-process slicing commands into raw stepper commands, allowing you to squeeze some extra speed out of an 8-bit mainboard, or get some new interesting features. There are also many web interfaces for Klipper which, in my opinion, tend to look better than Octoprint does.
  • mjpg-streamer - this tool allows you to plug a real USB webcam (no “3D Viewer” required!) and stream it to your computer, complementing an Octoprint or Klipper-type dashboard. This doesn’t necessarily depend on the first two - you could also use your WB-01 to add a camera to a printer controlled by a Pi Zero, which may be sufficient to control the printer but not to also run a camera at the same time.

Of course there’s nothing stopping you from setting up any software in the vast array supported by OpenWrt - though you should read the * Custom software section first if you intend on doing that.

Prerequisites

At the bare minimum, you’ll need:

  • a Creality WB-01 box. You do not need to set it up in the Creality Cloud app, in fact you don’t even need a smartphone, and I would recommend not doing so, in case Creality decide to push a firmware update making loading third-party software more difficult (I have not been made aware of this happening - if it does, let me know!)
  • a microSD card - doesn’t need to be particularly large, mine only uses 230 MB (you might have one in your printer already, but you should probably leave it there to make running Octoprint usable). Pro tip: if you’re using a third-party mainboard, you probably already have two of them.
  • some sort of microSD card reader, or an SD reader with an adapter. I use the one that came with my printer and it works.
  • a device capable of formatting an SD card as FAT32, copying files to it, accessing Ethernet networks, and connecting with ssh. Windows, macOS and Linux all work fine.

Flashing OpenWrt

To install OpenWrt on this device, you’ll first need to download it. The easiest way is to download it from snapshots server. You can compile it yourself if you want, more on that in this post. I recommend going that route if you can since the way snapshots work the package repository can be a bit unreliable so having your own ipk files to install makes things easier. Otherwise, download the tar.bz2 file.

However you get it, you’ll find that the main update file comes in a .tar.bz2 format, don’t extract it! In fact the printer will extract this itself when we set it up. Rename the file exactly to cxsw_update.tar.bz2, and put it in the root (no folders) of a FAT32-formatted microSD card. Then simply plug in your Creality Box, wait for a few minutes for it to turn on, and put in the microSD card. It should automatically detect it and eventually flash the custom OpenWrt firmware. Don’t unplug it or reset it while you wait; it will reload automatically when it’s done.

Once it’s done, connect your ethernet cable from the box directly into your computer. Log into it and you can begin setting things up!

Setting up the Wi-Fi

For the first part of the setup, to make things easier we’ll configure OpenWrt with the web interface, LuCI. You should be able to find this by typing 192.168.1.1 in your browser. If your home router also uses this address, it might not work, in which case you should disconnect from it for now (we’ll be fixing that in a bit!). There is no password by default: feel free to set one if you want, although it’s not strictly necessary.

The first thing you will want to do is go to Network > Interfaces, and under the LAN interface, press Edit. Then, change the IPv4 address to 192.168.2.1 or some other address which is not used by your LAN. Save that, and save and apply the page.

Next, go to Network > Wireless, Enable the interface if it isn’t already, press Scan, and select your wireless network to join it (note that the WB-01 only supports 2.4GHz). You should now see three sections: one radio, the OpenWrt SSID and your SSID. Disable the OpenWrt SSID if it isn’t already and save again. Now your device is securely connected to the internet over Wi-Fi! TODO: DHCP wireless

Setting up extroot

At this point you essentially have a mediocre wireless router, and you can run a lot of things on it, but if you want to use it for 3D printer management, you will quickly realise the device has a limitation: 16 megabytes of flash is nowhere near enough! Therefore, we will set up the microSD card to be able to install software and configuration on it, which can obviously be a lot bigger, allowing enough space for Python and other large applications. In OpenWrt, we do this with a process called extroot. You can read more about this on the OpenWrt wiki, but if you’re using my image I’ve installed all the packages for you so you can just run this sequence of commands on the box. To do this, you need to use ssh, which is a tool that allows you to get remote access to a machine through the command line. Windows 10, macOS, and most major Linux distributions include it: you should just type ssh root@<ip address> on the command prompt or terminal to connect. Then run this sequence of commands, one at a time. This will delete everything on the microSD card! Back up anything you care about.

DEVICE="$(sed -n -e "/\s\/overlay\s.*$/s///p" /etc/mtab)"
uci -q delete fstab.rwm
uci set fstab.rwm="mount"
uci set fstab.rwm.device="${DEVICE}"
uci set fstab.rwm.target="/rwm"
uci commit fstab
mkfs.ext4 -F /dev/mmcblk0p1
DEVICE="/dev/mmcblk0p1"
eval $(block info "${DEVICE}" | grep -o -e "UUID=\S*")
uci -q delete fstab.overlay
uci set fstab.overlay="mount"
uci set fstab.overlay.uuid="${UUID}"
uci set fstab.overlay.target="/overlay"
uci commit fstab
mount /dev/mmcblk0p1 /mnt
cp -f -a /overlay/. /mnt
umount /mnt
reboot

After running this, the box should reboot, and when you get back into LuCI, you should see however many megabytes or gigabytes you have available. Therefore you should now have space to put advanced software on your Creality Box! It’s not just software though; the extroot overlay will also store your data.

Installing software

Unfortunately this is pretty much where the fun part of the story ends. Software that you might want for 3D printing is difficult to install and if you can it probably won’t work very well. Someone should probably develop a really minimal OctoPrint alternative - just a simple screen with printer stats, a way to upload G-code to the printer SD (would streaming it work? I doubt it), maybe a webcam video, but it shouldn’t need to hog 100% of the CPU like Octoprint does. If such a software exists do let me know!

Octoprint

Octoprint is the most popular 3D printer remote control software, as it works pretty much plug-and-play with the majority of 3D printers using the normal Marlin firmware, including the stock firmware on most printers nowadays. I could get it to install, but I’ve moved the instructions to the compilation guide because you probably don’t want to use it! It’s unbearably slow to start up in my experience. When I got it loaded, it did seem to work albeit still slowly. I don’t have any non-Klipper printers to test it with so I’m not sure if it actually works, but I imagine the most you’ll get out of it is status updates or potentially uploading prints to SD. Have a go if you like but I don’t think it will go very well.

Klipper

I couldn’t get Klipper to work - it seems to be missing a Queue library, which I think is part of greenlet, which I haven’t managed to install yet (probably possible with python3-packages). Not sure though.

mjpg-streamer

This is what I’m using my Creality Box for - it’s already well supported in OpenWrt and it’s a good augment to a better computer which you could run Octoprint or Klipper on. I use a Raspberry Pi 3A+ for my Klipper device, and while it probably could handle a webcam, the way I have the power connected means it doesn’t connect a lot of the time, so I use the Creality box instead. This would probably be even better with a Raspberry Pi Zero, which is known to stutter prints when you run a video feed simultaneously. You’ll want to install kmod-video-uvc, mjpg-streamer, mjpg-streamer-input-uvc, mjpg-streamer-output-http, mjpg-streamer-www-simple and luci-app-mjpg-streamer for a nice GUI.

Custom software

In theory, installing anything you want on OpenWrt should be as easy as searching it up in LuCI’s convenient package installer and downloading it there. In fact, if you’ve ever used a stable OpenWrt version then you will have had this experience. Unfortunately, the WB-01 is not yet supported by a stable version of OpenWrt, it is using the snapshots, and the package situation there is a lot messier. The problem is that many packages depend on the kernel from where the software gets built being exactly the same as the one you’re running, and of course the window in which this will be true is usually less than a day after building it. That means if you went the route of compiling OpenWrt and installing packages from the official snapshot repository, you would basically have to rebase, recompile, reflash, reconfigure, redo extroot, and reinstall everything, every time you wanted to install a new package. I don’t think there’s any way to avoid that until the device lands in a stable version: you’ll just have to make sure you compile everything you need in advance.

Things you should know

  • You can upgrade the firmware in theory, use the sysupgrade.bin for that, but bear in mind that whenever you do this it will wipe all of your settings (there is a tick box to try and avoid that, but if you’re on extroot it probably won’t help), so I only recommend doing it if there’s a security issue, a bug, or a new feature.
  • The device’s performance is less than stellar: it sits somewhere between the original Pi B and the Pi Zero. One thing you could do is use it as an auxiliary device: I’ve been using mine as a camera server for my main Pi 3A running Klipper for a while now and it works fine for that.

Compiling OpenWrt for the Creality WB-01: tips and tricks

Recently, I investigated the Creality “Wifi Box” WB-01 and put OpenWrt on it. For the most part, if you want to run OpenWrt on it too, you’ll be satisfied with the images I built in my main guide, but if you want to build your own, such as to install custom software or overcome the limitations of OpenWrt snapshots, this guide is for you. It’s basically just a list of things you might want to do when you compile your image.
  • Obviously you’ll want to start on OpenWrt’s developer quickstart, that goes over the basics of compiling an image. I compile my images on Manjaro Linux, but any Linux should work. Don’t know if macOS works. Previously I compiled them on Windows Subsystem for Linux (WSL2), and it did work, but since it’s virtual machine based it has a tendency to run out of memory and slow your system down to a crawl, so I don’t recommend it.
  • The target you’re looking for is Mediatek Ralink MIPS > MT76x8 based boards > Creality WB-01
  • OpenWrt’s web interface LuCI is not enabled by default, so you will want to enable it.
  • The default behavior of OpenWrt is to disable the wireless radio on devices with an Ethernet port. To bypass this, create files/etc/config/wireless with a configuration file.
  • The list of extroot packages can be found on the extroot page - there’s enough room to compile all that in.
  • To compile Octoprint I used python3-packages. It’s designed for development purposes and it will break a lot of Python packages if you use it, so if you actually want to use Octoprint you should then you should probably write a package specifically for it and its dependencies. To use it you’ll need to enable “Advanced configuration options (for developers)” and then you will find it under Languages > Python.
    CONFIG_PACKAGE_python3-packages=m
    CONFIG_PACKAGE_python3-packages-list-host="cffi regex"
    CONFIG_PACKAGE_python3-packages-list="OctoPrint regex"
    CONFIG_PACKAGE_python3-packages-extra-deps="libc.so.6 libpthread.so.0"
    CONFIG_PACKAGE_python3-packages-pip-opts="--no-binary=regex"
        

    After

Getting into the Creality WB-01: my thought process

Recently, I investigated the Creality “Wifi Box” WB-01 and put OpenWrt on it. Along the way, I uncovered a few oddities with the device which I thought would be interesting to share for anyone else interested in it, or embedded development in general. I’ve also documented my thought process (this was written after the fact but it’s pretty much accurate).

The entry point

I actually started hacking this device before I even bought one. Details on the device online were scarce, and I wanted to ensure that its hardware platform would be hackable, otherwise there wasn’t much point. At this point I was speculating on if the device even ran Linux at all, and I was concerned by a competing device, the Geeetech 3DWiFi, which is based on a less flexible STM32+ESP32 based design. (Despite that, if you aren’t willing to tinker, it’s probably better thought through software wise, although I’ve never tried one) I thought my best way in would be through decompiling the Creality Cloud Android app, which interfaces with the printer, using apktool, and through searching the strings I found a link to the firmware download location.

The file format of the device’s firmware updates are nonstandard; it is simply a tar file containing a script called install.sh, which is executed with 2 arguments: whether or not the update was called via the IoT application (more on that later) or not, and the directory where the files in the tar were extracted. The other files that Creality uses in their packages are bImage and root_uImage, which contains the bootloader - yes, they update the bootloader for some reason - and the kernel and root filesystem image respectively. I extracted root_uImage with ~binwalk~, a nice tool designed for extracting all kinds of firmware images.

Exploring the filesystem

/images/cxsw-fs.png

You probably recognise this - that’s right, it’s a Linux filesystem! There is a bit of an oddity in etc_ro, which seems to just be where some etc files are, but nonetheless I had a vague idea of what I should look at first. Starting with /home, this seems to be where most of the binaries are for the IoT application. Unfortunately, these were all compiled, so I couldn’t really learn much about that - I think someone else should reverse engineer it at some point, it would not surprise me if it had security issues. In the strings of iot-linkkit-solo I did see strings relating to MQTT, and URLS for an “aliyun” MQTT server, I think it’s talking about this SDK from Alibaba Cloud. In prt_com I saw Creality, various model names of Creality printers, SOVOL-O2, which is a bit strange as it isn’t Creality, Marlin, and other related strings, which seems to indicate the printer/mainboard lockdown noted by others was intentional. (That said it might not be entirely malicious, the app could just have compatibility issues? No idea really.)

I moved on to /etc_ro/lighttpd/www, which told me that the system is based on a Ralink SDK of some sort. Opening the index.html file, I recognised the interface from a LiveOverflow video, and while Creality had hilariously commented out the main vulnerability from the menu, the System Command page was still there and could be navigated to manually. As a result, the device was going to pretty much wide open, so I decided to buy one. Of course being that Creality was “launching” this device for the 11/11 super sale (it seems to have existed since September? not really sure what was up with that) and I was ordering from China, I had to wait quite a while for mine to arrive. If you’re interested in one, they seem to have popped up locally on eBay by now, probably in part due to the fact that Creality has pushed to bundle them with many printers.

The hardware

In the meantime, I decided to look into the bootloader of the device. Basically this was just grepping for strings again - I could see that Creality had put the effort into compiling their own U-Boot, but apart from that I just got some part numbers of SPI flash chips, and a model number - MT7628.

Of course, when mine eventually arrived, I immediately set to taking it apart, and while there is no MT7628, there is an MT7688AN chip - as far as I can tell, the MT7688 series is just a revision of the MT7628 and they are for all intents and purposes identical. There is also RAM and the BY25Q128 flash chip, if you want to have a look inside there are teardowns on the Internet. I broke the front panel off mine because I didn’t really have a clue what I was doing so it just sort of sits in there now - makes opening it a bit easier I guess :)

Getting in

When I plugged the device in, at this point I was unsurprised to discover that it shows up as an access point - CXSWBox-FCEEE60017D4 (of course yours will show your MAC address). The device shows up on 10.10.10.254 and I had no problems getting in to the system command page. At the time, I had read the startup script and knew telnet wouldn’t automatically restart, so I just killed it and made a new telnetd -L /bin/ash to bypass the login prompt. Since then, a mysterious Hackaday user cracked the default root password: it’s cxswprin, so this step isn’t necessary anymore (or until they change it, anyway). Once I had a root shell, my first step was to make a flash backup, generally following these instructions - that came in handy later ;) I should have ran ifconfig here to check the MAC assignments but I forgot, so I had to do that later when I reflashed the stock firmware. I also did cat /proc/mtd to check the flash layout. But of course the main thing I wanted to do now was create an OpenWrt image for it and flash it.

OpenWrt

The OpenWrt project makes it very easy to add support for a new device - you can read my branch in a few minutes if you want to know all the details, but in effect you just copy a device’s dtsi from the same chipset and change a few variables like the MAC location, flash layout, LED assignments, and switch configuration. I got it done in less time than it took to actually run make and compile the image. You’ll probably want to use make menuconfig to set a few things up, like installing LuCI and the filesystem tools for extroot - check out my blog post about that.

When it was done, I used the mtd_write binary on the stock firmware to write it. The stock firmware divides the mtd regions of Kernel and RootFS so I had to write a little shell script to write to both partitions. Unfortunately the first time I flashed I specified the whole flash region which was a big mistake.

Getting in (again)

Yes, I did the stupid thing: I bricked it. I had overwritten the bootloader and the configuration, so even if serial recovery worked (I haven’t actually tried it, but I’ve heard it’s broken) it wouldn’t be an option here. Instead I had to reflash the SPI flash chip. Thankfully I had made a backup, so all I had to do was reprogram it.

In my case, I purchased the “CH341A MiniProgrammer” from Amazon, but I would not recommend doing the same. The design sends 5V into the chip instead of 3V, and while I’ve heard this works, it’s probably unhealthy for the chip and the datasheet suggested otherwise, and I didn’t want to risk having to wait several more weeks for another box from China, so I got a friend to perform a mod which converts it to 3V. Technically this worked, but in my case it was incredibly fragile and my friend had to resolder it several times, and had to dig into the leadframe to get more metal exposed. Might be that my friend is a bad solderer, but either way it doesn’t seem to be a particularly good idea. In addition to that, for some reason the way mine was set up, with pin 8 connected the chip would just short and get very hot - it didn’t kill the chip but it could have killed me, I burnt my finger on it a few times :P. As a result I had to wire a little proxy cable to disconnect pin 8 from the programmer. What’s weird is pin 8 is supposed to be VCC but nonetheless disconnecting it made the flash go flawlessly.

If you have to do it, I would recommend going a different way: I’ve heard good words about FTx232H based solutions, and you could even use a Raspberry Pi you have laying around.

You’ve probably never heard of BoyaMicro before, and neither have any flashing programs I’ve seen, including flashrom, but fortunately its open source nature means a contributor called cecada has added support for this chip. Thanks to them, I could very easily read from the chip, and my backups were consistent so I felt confident to write to the flash chip, and thankfully it worked successfully!

The first flash

After I got back in, I repeated the setup and flashed OpenWrt successfully. However, for some reason any configuration changes I made did not persist. After looking at the dmesg logs, I figured out it was because spi-nor doesn’t recognise the obscure flash chip from before. As a result, I had to reprogram the SPI chip to go back to stock again, and then wrote a kernel patch to add support using the ID from the datasheet. Once I had done that, I was golden! At this point, I was pretty much done - everything else was more about OpenWrt then this device specifically, and I’ve covered that in my other blog post.

Following are a few miscellaneous oddities I discovered during the process:

The MAC mystery

This is one of the things about the Creality box where I literally have no idea. Perhaps someone with more familiarity with these chipsets can shine a light on it. When I was configuring the dtb for OpenWrt, I wanted to know where the MAC addresses were so they could be read. I took a look at the flash backup and at 0x40000 it looks like this:

00040000: 2876 0600 fcee e600 17d4 0000 0000 0000  (v..............
00040010: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00040020: 0000 0000 2000 0000 fcee e6ff 17d4 fcee  .... ...........
00040030: e640 1b06 1134 0020 ffff 0001 0000 0000  .@...4. ........
00040040: 0000 2200 0000 0000 3000 0000 0000 0000  ..".....0.......
00040050: 8200 0094 40b9 c0ca 1e81 8181 40ca 2080  ....@.......@. .
00040060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
...

There are 3 places which contain the sequence fc:ee:e6, the OUI section of the MAC address (attributed to manufacturer Formike Technologies):

  • +0x04 has the MAC address fc:ee:e6:00:17:d4, which is the real MAC address, used on the stock firmware for the Wi-Fi and on the label, SSID and QR code (which is just the SSID and the serial number encoded)
  • +0x28 has fc:ee:e6:ff:17:d4, this variant is used on the Ethernet side of things.
  • +0x2e has fc:ee:e6:40:1b:06. This one is very strange - despite having an OUI and being some sort of data, I cannot find anywhere where it is actually used. I have no idea why the manufacturer would burn a useless MAC address on every device manufactured. It could also be the same on every device, but I only have one so I have no way to tell.

Speaking of weird device-unique data, the aforementioned serial number in the QR code is nowhere to be found anywhere on the actual device. My guess is that it is only stored on Creality’s servers after manufacture as a sort of authentication code, so that a malicious actor can’t just register all the MAC addresses onto their account.

Firmware updates

The way the device handles firmware updates is frankly just bizzare to me. First of all, there is no need for the device to trust user input with the install script - why didn’t they just have one hardcoded on the device end? Heck, one could consider Creality’s own update script untrustworthy, as for some reason, it updates the bootloader of the device which probably significantly increases the brick risk if something goes wrong during the write. Not that the bootloader’s recovery functionality works anyway; I never managed to invoke it, but I’m not an expert in U-Boot.

Thoughts

Document your projects

I’ve had an idea in my head for a while, and now I’m finally going to do it. I’m going to crawl through my internet history and document every project I’ve ever done.