mirror of
https://github.com/dragonlock2/dragonlock2.github.io.git
synced 2026-06-28 02:38:34 +00:00
move posts to folders by year
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: AUVs 2019 Power Distribution Board
|
||||
date: 2019-01-31
|
||||
categories: school
|
||||
excerpt: A brief overview of the power distribution board I designed for our sub.
|
||||
header:
|
||||
teaser: /assets/img/2019/auvs-2019-pdb-soldered.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
One of things we needed for our sub was a power distribution board. It would serve as the central hub for any and all things power. Working off of our power consumption spreadsheet, Mark and I developed specifications for the board including size and current capacity. We also specified what voltages needed to be supplied and how many components were to be connected to those power rails. With all the requirements listed, I headed straight to KiCad. This was my first time designing a power distribution board, so I learned quite a bit about copper plane placement and trace current capacity. I based the design off of a FIRST robotics board I found online.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auvs-2019-pdb-kicad.jpg" %}
|
||||
|
||||
We were really grateful that Bay Area Circuits sponsored the manufacture of our board. Our main concern was current capacity and they helped out wonderfully with helping us make sure our board could handle everything. After sourcing the parts, we got started assembling the board.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auvs-2019-pdb-soldered.jpg" %}
|
||||
|
||||
After the RoboSub competition in early August, we definitely had a lot of things we wanted to change, including the power distribution board. It performed admirably, but we now need to specify newer requirements. Time for next year!
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: CalSol MPPT CAN Converter
|
||||
date: 2019-03-22
|
||||
categories: school
|
||||
excerpt: A little board I designed for CalSol.
|
||||
header:
|
||||
teaser: /assets/img/2019/mppt-can-v1.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
In CalSol, this is a little board I designed to convert the higher frequency CAN signals coming off the MPPT to the lower frequency CAN signals the rest of the car uses. I think this was for Tachyon. It was really nice working with other people to develop specifications for a board that would fit into a larger whole. I had to head home for the summer so I didn’t have time to program it or do most of the soldering.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/mppt-can-v0.5.jpg" %}
|
||||
|
||||
After some revising I ended up with this.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/mppt-can-v1.jpg" %}
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Adventures in Lock Picking
|
||||
date: 2019-04-20
|
||||
categories: other
|
||||
excerpt: A retelling of my adventures in lock picking during the school year. Of course, I made sure I didn't do anything illegal.
|
||||
header:
|
||||
teaser: /assets/img/2019/lockpicking.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
Note: I make no guarantee that any of this actually happened in real life. Any resemblance to reality is a coincidence. I have a very vivid imagination. Additionally, pictures may be staged.
|
||||
|
||||
One of the skills I picked up during the school year because everyone kept locking themselves out was lock picking. It all started when I locked myself out of my room one day and couldn’t get my roommate to come soon enough. I got the pick set from my friend and immediately got started. Within 10 minutes, I was in. Not a bad first try.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/lockpicking.jpg" %}
|
||||
|
||||
During spring break I attempted to pick my friend’s door, with his permission of course. Unfortunately for me, I was caught by the RA and got written up. I had to meet with the residential director of Foothill for a hearing. At first I was prepared to be really rebellious and argue back with solid facts, but upon finding out that the director was acting as judge, jury, and executioner, I made sure to play nice. What the director did not need to know was that I’ve successfully lock picked like 10 times before. Funnily enough, most people get sent to the residential director for alcohol, not lock picking. After the hearing, I was emailed my conviction of tampering along with my punishment. They like to call it a “sanction” because it sounds less mean.
|
||||
|
||||
My “sanction” was to write a 500+ word essay about university policy on tampering, how it applied to my case, and what would happen if it didn’t exist, all without justifying my actions. Here’s what I sent in [Tampering Policy.pdf](/assets/pdf/Tampering-Policy.pdf)
|
||||
|
||||
I had way too much fun writing this essay. Ever since my SciOly days, I’ve loved picking rules apart. It took me two hours but it was well worth the laugh. I even made sure to employ the slippery slope fallacy at the end to really nail my point home. Surprisingly, my essay actually got accepted. I never lock picked ever again.
|
||||
|
||||
One line I didn’t have room to put in was about B5. Keys. It states, “Possession, duplication, misuse of University issued keys and key cards, including loaning keys to any other person, or leaving a key unattended in the lock, is prohibited.” Weird grammar aside, according to the policy, possession of university issued keys and key cards is prohibited. Thus we can’t have the keys to our own rooms. Even reading the policy differently, where possession, duplication, misuse are considered one act to solve the issue of possession individually being prohibited, issues still arise. Now I can duplicate a key without issue as long as I don’t misuse it.
|
||||
|
||||
Funnily enough, keys stamped with “Do Not Duplicate,” are not legally binding and are a very poor security measure. According to [this](https://blog.key.me/do-not-duplicate-keys/), these keys tend to be the least secure, which explains my ease with picking dorm locks. So much for excellent dorm security.
|
||||
|
||||
Later in the year I found out that Blackwell, our newest dorm, used RFID keycards for their doors. This was particularly intriguing. I found out that they used MIFARE Classic cards, whose security has been broken for over a decade. Trying to crack one using MFOC, it turned out that the card was actually a MIFARE Plus, which solved the security vulnerability of the past. After hours of research and emailing a dude on the internet for files, I was able to perform the newer hardnested attack on the card. 15 hours later, I was in. I copied the info over to a MIFARE Classic card.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/lockpicking-rfid.jpg" %}
|
||||
|
||||
The copy only ended up working on the ground floor stairs and elevator, but that was still pretty cool. The rooms and lounges I believe use a reader that rejects MIFARE Classic, which is awesome. If the university wants to further improve security, they should upgrade the ground floor readers. However, I do believe the better readers can be circumvented if I got access to blank MIFARE Plus cards.
|
||||
|
||||
Of course, I should reiterate what I said above about making no guarantee that any of this actually happened. I just love security and I have a very vivid imagination. Perfect combination to tell a story.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Micromouse DeCal
|
||||
date: 2019-05-07
|
||||
categories: school
|
||||
excerpt: A class on the Micromouse competition. Map out a maze, solve it, then speed through it.
|
||||
header:
|
||||
teaser: /assets/img/2019/micromouse-sp19.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
I had quite a bit of fun in this decal. Our goal was to make a robot that would go through a maze and map it out. Then it would go through again as fast as possible. Fastest robot wins.
|
||||
|
||||
Working in groups of three, we put together a predesigned robot and proceeded through several labs from motor control to wall following. It was pretty cool working on the control systems for the motors. We used these really awesome time of flight sensors to do wall detection. Honestly, with my experience in Arduino most of the code was pretty trivial, but I still did learn a thing or two. Unfortunately, there wasn’t enough time to implement the maze solving, but we did get to wall following and turn detection.
|
||||
|
||||
{% include video id="4tAVbP4a6mA" provider="youtube" %}
|
||||
|
||||
{% include figure image_path="/assets/img/2019/micromouse-sp19.jpg" %}
|
||||
|
||||
There are quite a few things I would change with the robot’s design, but overall it proved a great learning platform.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: AUVs 2019 Main Electronics Assembly
|
||||
date: 2019-05-12
|
||||
categories: school
|
||||
excerpt: A brief overview of the work I did on the main electronics assembly in our sub for the RoboSub 2019 competition.
|
||||
header:
|
||||
teaser: /assets/img/2019/auv-2019-tube-assembly.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
For the electronics that would fit into the main tube, I worked with Mark to take specifications from other subteams to create a list of parts needed as well as other considerations to be taken into account. Our basic plan for the mechanical side was to use a tiered shelf system to place parts. We also planned out the wiring diagram for the penetrators at the ends of the tubes. Afterwards, I threw everything into Fusion 360 to organize everything as best as I could to balance ease of manufacture, repairability, thermals, and extra space for future improvements.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auv-2019-electronics-cad.jpg" %}
|
||||
|
||||
After designing it, I trekked over to the Invention Lab to print out some of the parts. Originally I wanted to 3D print everything, but we ended up cutting sheets of ABS to make the shelves.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auv-2019-tube-assembly.jpg" %}
|
||||
|
||||
At competition, the sub performed admirably considering it was our first year there. Based off of observations of all the other teams there, we definitely have a laundry list of things we want to change for next year. To improvement!
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auv-2019-water.jpg" %}
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: EE16B
|
||||
date: 2019-05-18
|
||||
categories: school
|
||||
excerpt: A brief overview of the voice controlled robot that took up most of the semester's lab time.
|
||||
header:
|
||||
teaser: /assets/img/2019/ee16b.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
I had a lot of fun during EE16B, especially the labs. One of the refreshing parts of the labs was that everything was done on breadboards. I usually go straight to perfboards or PCBs whenever a messy prototype works, so this was an intriguing change. We did a bunch of smaller labs in the beginning, but the main part of the semester was building a voice controlled robot. It combined basically every part of what we learned during the semester into one neat package. I’ll just give a brief overview of the different parts of the robot.
|
||||
|
||||
We started off with system identification of the motors. First we collected a bunch of data on the velocity of the wheels based on different PWM signals. We then selected a small range that was roughly linear so that we could perform least squares and develop a linear model. From this linear model we were able to select an operating point, a velocity that both wheels could reasonably achieve. This would offer us the best chance at keeping the car straight and correcting for errors.
|
||||
|
||||
After system identification, we implemented open loop control (i.e. not using encoders) to move the car. As expected, error accumulated over time and the car did not go straight at all. One thing to note is that motors tend to need a “jolt” to start up because of friction. Jolt application tends to be uneven, but we would account for this by considering it as part of the error in our closed loop testing.
|
||||
|
||||
Since open loop didn’t work so well, we implemented closed loop control, taking into account the encoders to measure error. It was really interesting solving equations to determine parameters for k values and tuning the loop.
|
||||
|
||||
{% include video id="cqi623zFri8" provider="youtube" %}
|
||||
|
||||
The next part would be implementing voice control. We chose 4 different words that we thought would have very different waveforms: EECS, Berkeley, Stanford (pronounced “Stanfuuurd”), and lockpicking. After recording about 30 samples of each word, we threw it into the iPython notebook to run PCA (Principal Component Analysis) to pick out the two main components that would help us distinguish words. After that we used centroid finding (which we actually learned in CS61A!) to complete our classifier. The likeliest word said in a recording is that which is closest to the corresponding centroid.
|
||||
|
||||
{% include video id="JbHR8M3882I" provider="youtube" %}
|
||||
|
||||
Finally, we integrated all the systems together. Our robot actually worked pretty well like half the time. Surprisingly, I don’t actually have any videos of the whole system integrated together.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: CS61B - Data Structures
|
||||
date: 2019-05-19
|
||||
categories: school
|
||||
excerpt: An excellent class all about data structures.
|
||||
header:
|
||||
teaser: /assets/img/2019/cs61b-proj3.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-23-19</sub>
|
||||
|
||||
A class all about data structures. I learned so many things in this class that I didn’t know before. We covered so many different data structures, their applications, and run times among other things. All of our homeworks, labs, and projects served to apply what we learned and they really were particularly fun challenges to solve. I can’t really post any code, but I’ll just write a little about the more interesting homeworks and projects we did. Mostly the stuff with graphics because then I can post pictures.
|
||||
|
||||
## Project 0: NBody
|
||||
|
||||
Simulating bodies in space to provide a crash course Java. Pretty much just followed the instructions, but it was still pretty cool.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/cs61b-proj0.jpg" %}
|
||||
|
||||
## HW2: Percolation
|
||||
|
||||
This was a particularly fun and challenging homework to get all the points for. The premise of the homework is to find when a system percolates, which you can imagine as water flowing from the top to bottom through openings. It was a pretty cool application of Disjoint Sets. The gist of the solution is to connect the top and bottom rows to virtual points and whenever these virtual points are connected the system percolates. One issue that I ran into was preventing backflow, which is when the liquid flows through the virtual bottom point and back up. My solution was to use another Disjoint Set to keep track of backflow, which was actually a pretty popular solution. It used more memory, but worked perfectly.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/cs61b-hw2.jpg" %}
|
||||
|
||||
## HW3: Hashing
|
||||
|
||||
One of the more conceptually interesting data structures, well they’re all interesting actually, are those that involves hash codes. It’s pretty crazy to have a data structure that has an O(1) average search and insert and delete. We also learned more about hash functions and how there must be an overlap so hash functions should be chosen to have an even spread.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/cs61b-hw3.jpg" %}
|
||||
|
||||
## HW4: AStarSolver
|
||||
|
||||
Using MinPQs to help implement the A\* algorithm, we were able to solve a bunch of cool puzzles that from a certain point of view could be viewed as trying to get from one place to another with the minimum cost. We also made it memory efficient by only ever adding neighbors as they came along. We were using A\*, but a lot of times we had a constant heuristic function so it reduced to good old Dijkstra’s algorithm. There’s no fancy graphics this one, but solving puzzles was pretty dang cool.
|
||||
|
||||
## Proj2C: Bear Maps
|
||||
|
||||
Combining a lot of what we learned before from MinPQs to A\* to Tries, we implemented a basic maps system. The biggest challenge of this part of the project was to pick all the images to display based on the zoom level. It was a bit tricky working out all the bugs, but I got it eventually.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/cs61b-proj2c.jpg" %}
|
||||
|
||||
## Proj3: BYOW
|
||||
|
||||
Last but not least and perhaps the most fun project of the semester, we got to make our own game, complete with randomized world generation, a save system, and enemies that chase you around. We were given a lot of flexibility with this project, including many different challenges we could complete for various amounts of points. Since this was quite a big project, my partner and I spent a lot of time thinking about the implementation. Some of the included methods pointed us into the right direction of well organized code that was easily modifiable to do a lot of the ambition points.
|
||||
|
||||
The world generation was probably the most complicated part of the project. We started by making rooms of various sizes and then connecting them with hallways. We were inspired by a friend of our’s idea to do something like raytracing to do hallway generation. Taking this idea and adding a couple of our own randomizations, we ended up with a nice world generator.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/cs61b-proj3.jpg" %}
|
||||
@@ -0,0 +1,196 @@
|
||||
---
|
||||
title: PCB Laminator
|
||||
date: 2019-06-05
|
||||
categories: projects
|
||||
excerpt: After trying basically every other method in the book, I finally made a tool that allowed me to make PCBs at home easily, reliably, and precisely.
|
||||
header:
|
||||
teaser: /assets/img/2019/pcb-lam-v1.5.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/pcb-hybrid-mill.jpg
|
||||
- image_path: /assets/img/2019/pcb-hybrid-cutout.jpg
|
||||
- image_path: /assets/img/2019/pcb-hybrid-etched.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2019/pcb-lam-amazon.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-amazon-teardown.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-electronics.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-oled.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-thermistor.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-overall.jpg
|
||||
|
||||
gallery4:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-test-1.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1-test-2.jpg
|
||||
|
||||
gallery5:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-cad-template.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-motorcad.jpg
|
||||
|
||||
gallery6:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-kicad-schem.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-kicad-model.jpg
|
||||
|
||||
gallery7:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-transfer.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-etch.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-after-etch.jpg
|
||||
|
||||
gallery8:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-pcb-assembled.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-assembled-bottom.jpg
|
||||
|
||||
gallery9:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-cad.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-half-assembled.jpg
|
||||
|
||||
gallery10:
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-simulink.jpg
|
||||
- image_path: /assets/img/2019/pcb-lam-v1.5-simulink-final.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-22-19</sub>
|
||||
|
||||
One of the things that I have never been able to get working reliably has always been homemade PCBs. Over the years I’ve tried basically every method in the book. For projects that need multiple copies of a board or are quite complicated and compact, sending it off to a manufacturer is both time and cost effective. However, generally speaking a single sided board with 7 mil trace and space is good enough for most prototyping needs. The fast turnaround time for a DIY PCB is also unbeatable. With my furthered exploration into all things surface mount, it became even more imperative to come up with a reliable method of PCB manufacture at home.
|
||||
|
||||
## Other Methods of PCB Making I’ve Tried
|
||||
|
||||
### Toner Transfer Method (Clothing Iron)
|
||||
|
||||
I started trying to make PCBs back in sophomore year of high school. Initially, I tried the ever so popular method of using a clothing iron to transfer toner to a copper clad board. I had far more failures than I had successes. It’s really difficult to get the perfect combination of temperature and even pressure. The issue was really the human component. Still, it was enough to get by until I had something better.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-tonertransfer.jpg" %}
|
||||
|
||||
### CNC Milling
|
||||
|
||||
Coming into senior year I was working more with SMD components so I definitely needed a reliable way of making PCBs. Using the CNC router I recently built, I tried milling my own boards. It was actually pretty reliable for a time, but I kept on breaking end mills and every time I messed up a board it’d waste a lot of material. Even more, while FR2 boards were reliably millable, the traces don’t adhere very well and the board’s heat resistance is poor. FR4 is a much better choice for my purposes, but it’s much harder to reliably mill.
|
||||
|
||||
{% include video id="SZC9APz4AzE" provider="youtube" %}
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-cncmill.jpg" %}
|
||||
|
||||
### Hybrid Mill and Etch
|
||||
|
||||
I ended up developing my own hybrid mill and etch method to reliably make FR4 PCBs. It also allowed me to go from a 12 mil trace and space to 8 mil. The method involved covering the board in Sharpie and then milling away the Sharpie and some copper if I could. Afterwards I’d throw the board into the etchant to get rid of the rest of the copper. This method was really nice because I didn’t have to drill any holes myself or cut the board to size. It also solved the issue of breaking end mills because I would never go too deep. And if I was too shallow, the etchant would just remove the rest of the copper.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
Overall, it was tedious, but very reliable.
|
||||
|
||||
### Conclusion
|
||||
|
||||
I also tried the no heat transfer method which involves pouring a mixture of acetone and isopropyl alcohol on top of the toner to dissolve it and transfer it to the copper. Safe to say it did not work. One method that I heard about that supposedly works really well is using a photoresist, but I didn’t want to try it because it could get expensive.
|
||||
|
||||
After all my attempts, I was still in search of a better method. I refined my requirements quite a bit. Most of all it had to be reliable and precise enough to make 8 mil trace and space FR4 PCBs. It also had to remove the human component in any areas that needed consistency. Another thing is cost and turnaround time. Getting the raw materials had to be widely available (e.g. Amazon) and cheap. The last thing that would be really nice is to be portable. Lugging a giant CNC router around is not portable.
|
||||
|
||||
## PCB Laminator v1
|
||||
|
||||
A couple weeks into winter break, I decided to give PCB making another go. I really liked the concept of the toner transfer method because of how simple it was. I just needed a machine to do the actual transferring. Doing more online research, I found out about people using off the shelf laminators to make PCBs. Having both high consistent pressure and even temperature application, it was the perfect method.
|
||||
|
||||
There’s pretty much no good how-to on making a PCB laminator, but the concept is pretty simple and widely applicable. The gist is to take a laminator and modify it to control its temperature and feed rate. Technically, people have gotten the method working without any modifications, but I liked having the fine adjustments. First, I had to choose a laminator and since I wanted one that was cheap and easy to find, I went with the [AmazonBasics laminator](https://www.amazon.com/AmazonBasics-PL9-US-Thermal-Laminator/dp/B00BUI5QWS/).
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
Taking it apart, I noted the relatively simple construction. Since I would be working with small boards, I’d have to either heavily modify the case or completely ditch it altogether to have closer access to the rollers. The heater would be pretty simple to turn on and off and the thermistor would be placed where the temperature controlled switches originally were. The one part that proved a challenge was getting fine control of the rollers using the existing AC motor.
|
||||
|
||||
I immediately got to stripping the electronics to just the heater and motor. I tried to learn how to reverse and slow down the motor but got nowhere. As a workaround, I ended up just turning the motor on and off at set intervals to get the speed I wanted. With the control systems worked out, I immediately got to soldering the electronics together. Since I couldn’t yet make PCBs easily, I used all THT components.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
||||
|
||||
For controlling the AC components, I used [BTA16-600B](http://pdf.datasheetcatalog.com/datasheet/stmicroelectronics/7471.pdf) TRIACs with [MOC3021](http://www.farnell.com/datasheets/97984.pdf) optoisolators. There’s also a zero crossing detector so that I could turn things on at a zero crossing, reducing strain on the TRIACs. I used a little snubber circuit on the motor TRIAC to correct for the inductive load. To power the microcontroller, I used an off the shelf USB charger. Some potentiometers and an OLED display would serve as the user interface. The main safety concern was the loose wiring and exposed AC power lines, but considering this was just a prototype, it was fine for the time being. Also, I was pushing the JST-PH connectors to slightly above their limits. Funnily enough, I didn’t have any heat resistant tape at the time, so I jerry-rigged the thermistor mount.
|
||||
|
||||
{% include gallery id="gallery4" %}
|
||||
|
||||
Overall, this prototype did not work very well if at all. The main issue was motor control. It didn’t respond well to being turned on and off all the time and would often get stuck. Also, it appeared that having a constant rolling pressure worked better, but since I couldn’t reverse the motor I had to stand there constantly watching the machine. Even more, the user interface I designed ended up being really clunky. Well, it was nearing the end of winter break so I had to set the project aside for the summer.
|
||||
|
||||
## PCB Laminator v1.5
|
||||
|
||||
Coming back in the summer I immediately got to work on revising my prototype. I started with attempting another transfer using the v1 hardware and I actually got a pretty decent result.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1-test-3.jpg" %}
|
||||
|
||||
Seeing that the method was viable, I continued on with the project.
|
||||
|
||||
### Motor Control
|
||||
|
||||
First, I had to solve the issue of motor control. I knew I had to ditch the AC motor that came with the laminator. I needed something that was easy to control, reversible, and in my parts bin. Initially, I considered using stepper motors, but none of the controllers I had used 5v and I didn’t have a small 12v brick so that wasn’t an option. The next choice was a servo or Vex motor. I had a bunch of Vex motors lying around and didn’t want to mess around with servo horns so I ended up picking the Vex motor. What’s really nice is that Vex motor controller 29 provides a cheap and easy way to control any DC brushed motor. I just had to make a slight modification to bypass the 4.5v linear regulator.
|
||||
|
||||
#### Mount and Gearing
|
||||
|
||||
The first thing to do was mount the motor. In order to prevent the heat from the rollers from melting any of my 3D printed parts, I made sure that anything that interfaced with the rollers was heat resistant. The main point of concern was the gear that meshed with the gears that drove the rollers. Luckily, one of the included gears had an 8mm ID, which meant I could connect it to a ubiquitous 8mm steel shaft. I cut a keyway into both the gear and shaft to fit a metal key and maintain a tight fit.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5-gear.jpg" %}
|
||||
|
||||
With that done I had to bring everything into Fusion 360 to design a mount. A lot of the spacing and dimensions were hard to measure well so I took a picture with my caliper for a reference dimension and imported it. Cross referencing it with actual measurements, I was able to create an accurate model of the laminator.
|
||||
|
||||
{% include gallery id="gallery5" %}
|
||||
|
||||
I made an early decision to use a rail system to mount everything because I had some 1/4″ shafts lying around.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5-structure.jpg" %}
|
||||
|
||||
Surprisingly, even though I used PLA for all my parts, they were all far enough away from any heat source that I didn’t have to use a more heat resistant filament.
|
||||
|
||||
#### Magnetic Encoder
|
||||
|
||||
In order to complete a closed loop system, I needed some way of getting feedback for the motor position. I had a bunch of magnets and a two Hall effect sensors left, so I decided to try my hand at making a magnetic quadrature encoder. The concept is pretty simple. Just have two sensors whose square wave outputs (or sine wave if you want to do some fancy interpolation) are offset by 90°. To do this I fit a ring of magnets around the bigger gear with alternating polarities to ensure a clean, perfectly spaced square wave at the output. To create the 90° offset, I made spaces to glue two Hall effect sensors in such a way so that when one sensor was completely in the middle of a magnet, the other was in between two others.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5-magnetic-assemble.jpg" %}
|
||||
|
||||
Although the resolution of the encoder would end up pretty low, it was very reliable, easy to assemble, and precise enough for the task at hand.
|
||||
|
||||
#### Closed Loop Control
|
||||
|
||||
In order to instruct the motor to go to a certain position as quickly as possible, I decided to take a page out of what I learned in Micromouse and implement a PI loop. Considering the pretty ideal operating conditions where stopping the motor would completely stop the rollers, I didn’t implement the integral completely accurately and just reset the integral when the error became 0. For tiny errors though, the integral component proved useful. Since the system was not very complex, I ended up tuning the PI loop by hand.
|
||||
|
||||
### Making the Electronics
|
||||
|
||||
With motor control done and knowledge of heater control from v1, I threw all the electronics into KiCad. I picked the Atmega328P-AU as the microcontroller because it had all the features I needed while being easy to program. Funnily enough, I actually ended up using almost all of its pins.
|
||||
|
||||
{% include gallery id="gallery6" %}
|
||||
|
||||
Most of the electronics are pretty much the same as v1, but in a nicer package.
|
||||
|
||||
#### Making the PCB
|
||||
|
||||
Using the v1 hardware to control the temperature and a separate Arduino for the motor, I was able to get a functioning prototype of the final laminator. It was also the perfect opportunity to test the whole process.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5-proto.jpg" %}
|
||||
|
||||
To my pleasant surprise, the transfer worked perfectly on the first go.
|
||||
|
||||
{% include gallery id="gallery7" %}
|
||||
|
||||
The rest of the assembly process for the electronics was relatively straightforward.
|
||||
|
||||
{% include gallery id="gallery8" %}
|
||||
|
||||
### Finishing Up the Hardware
|
||||
|
||||
With the electronics done and the motor control ready, it was time to put everything together into one compact package.
|
||||
|
||||
{% include gallery id="gallery9" %}
|
||||
|
||||
The main safety issue is still some exposed high voltage lines, but for the most part they’re tucked away. For maximum safety, in the future I can make a shroud for the electronics and ground any exposed metal.
|
||||
|
||||
### Completing the Code
|
||||
|
||||
To finish everything up, all I needed to do was combine my motor control code with my temperature control code and add a user interface. The first little thing I had to solve was voltage drops caused by the high current draw of the motor and insufficient power supply. Current spikes occurred whenever the motor experienced a sudden change in velocity. I ended up solving it by limiting the rate of change of the motor velocity.
|
||||
|
||||
The rest of the code was pretty straightforward. Part of it was getting pin change interrupts to work. Most of it was little user interface tweaks to make everything easy to use. My code can be found [here](https://gist.github.com/dragonlock2/61dbff048998acb8fdb6069bdab6dd0f).
|
||||
|
||||
#### Tuning the PID Loop
|
||||
|
||||
The last thing to do was tune the PID loop for the heater. One thing I heard about and always wanted to try was using a computer to build a model of a system and then solving for the PID constants. I found out that I could use MATLAB and Simulink to do just this.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5-systemid.jpg" %}
|
||||
|
||||
The gist of system identification is to collect a bunch of input and output data and throw it into PID Tuner to identify the system and then specify some parameters for the PID loop to get a desired response time and overshoot. After that, throw the constants into Simulink to see how the model behaves. One thing to keep in mind is that there is a maximum value for the input. You can’t turn the heater higher than a 100%.
|
||||
|
||||
{% include gallery id="gallery10" %}
|
||||
|
||||
I’m not completely sure why, but the values the PID loop puts out in real life tend to swing between the minimum and maximum. Still, the temperature graph matched the model pretty well, so it all works out. Low overshoot and fast response time, exactly what I wanted.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pcb-lam-v1.5.jpg" %}
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: TPS61201 - 3.3v Booster
|
||||
date: 2019-06-06
|
||||
categories: cool-chips
|
||||
excerpt: A nifty little chip whose versatile features gave it a very warm welcome into my parts bin.
|
||||
header:
|
||||
teaser: /assets/img/2019/tps-board.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-22-19</sub>
|
||||
|
||||
While taking apart an Amazon Dash button to see what chips I could salvage and learn about, I came across the chip that generated the 3.3v to power everything. Using info from this [website](https://mpetroff.net/2016/07/new-amazon-dash-button-teardown-jk29lp/), I was able to find that it was a [TPS61201](http://www.ti.com/lit/ds/symlink/tps61202.pdf). This was quite the fascinating chip since I was always interested in boost circuits and how to implement them into my own projects.
|
||||
|
||||
Checking out the datasheet, it looks like the TPS61201 and it’s family of chips is quite a versatile bunch. The TPS61201 specifically can supply a couple 100 milliamps, has excellent efficiency, both boost and down conversion modes, undervoltage protection and more all in one tiny package. The extra parts necessary to power it are also all quite simple and easy to route on a PCB. Even more it’s made by TI which has a very generous sample program. All in all, it had a very warm welcome into my parts bin.
|
||||
|
||||
Using my PCB laminator, I was able to turn around a test PCB rather quickly. A bit of soldering later and I was off.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/tps-board-test.jpg" %}
|
||||
|
||||
I also explored a bunch of boards from eBay that could generate 3.3v from a lower or higher voltage, but none could quite compare to the TPS61201. Of course, there are definitely better options for different use cases, but this is a great general purpose chip for my projects.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Arc Lighter v1
|
||||
date: 2019-06-21
|
||||
categories: projects
|
||||
excerpt: What better way to light things on fire than with electricity?
|
||||
header:
|
||||
teaser: /assets/img/2019/arc-v1.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/arc-v1-cad.jpg
|
||||
- image_path: /assets/img/2019/arc-v1-electronics.jpg
|
||||
- image_path: /assets/img/2019/arc-v1-assembled-no-electrode.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-21-19</sub>
|
||||
|
||||
Thinking back to my younger years, it appears that I always had a fascination with high voltage. Because of the incredibly high voltages they could generate, things like stun guns, plasma globes, and Tesla coils were absolutely awe-inspiring. Finding out about arc lighters was equally cool because they could generate around 10kV in the palm of your hand. Of course, that meant I had to figure out how to build one. Either from not wanting to wind my own transformer or always having other stuff to do, I never really got around to it.
|
||||
|
||||
## Reverse Engineering an Existing Design
|
||||
|
||||
Coming back for the summer after my first year at Berkeley I randomly decided, “Hey! Let’s take a look at that arc lighter again.” I had bought an electric flyswatter from Harbor Freight not too long ago so I decided to take a look at it. Taking a look at the circuit (after discharging the capacitor of course), I saw that the high voltage side capacitor was rated at 600v, so I assumed the output voltage was less than that. Hooking up my trusty Craftsman multimeter that had served me for almost a decade, I heard a pop and saw that my multimeter had broken. After a brief grieving period, I wisely decided not to hook up my expensive Fluke 117 but instead a cheap multimeter I got for free at school. That one broke too. I had a pretty powerful circuit on my hands, so I decided to reverse engineer it.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/lighterv1-sketches.jpg" %}
|
||||
|
||||
After analyzing the circuit and redrawing it, it became pretty clear how it worked. When the circuit is turned on, current flows through the feedback coil and through the base of the BJT transistor, turning on the primary coil. The magnetic field generated by the primary induces a reverse voltage in the feedback coil, turning off the BJT and thus the primary. The primary coil’s magnetic field collapses, and the induced voltage in the feedback coil drops. The BJT turns on again, and the cycle continues. This cycle of on and off in the primary coil generates the AC signal necessary to induce a higher voltage in the secondary.
|
||||
|
||||
## Building My Own Circuit
|
||||
|
||||
Doing some research, it seems like most people use some variation of this circuit for their arc lighters. To get the turns ratio necessary to produce a good arc, they usually took an existing one and rewound the primary with fewer turns.
|
||||
|
||||
That’s when I ran into my first problem. Arcs formed between the secondary’s coil windings when the voltage was high enough, effectively destroying the transformer. I did attempt repairs by soldering wires to the super thin magnet wire, but this fix proved only temporary. I tried using a CFL inverter transformer instead, which could handle higher voltages. It worked, but I still broke two of them before finalizing a working design.
|
||||
|
||||
Another issue I ran into was heat. If the arc was on longer than a couple seconds, the transistor would get too hot. Using MOSFETs produced satisfactory arcs but far more heat than BJTs. I searched for an improved circuit and found [GreatScott’s](https://youtu.be/lk_1lzMiUVc) which worked really well. The waveform at the primary was now a sine wave instead of the jagged one of the old circuit. I could even keep an arc going for about a minute before the MOSFETs got too hot.
|
||||
|
||||
## Putting It All Together
|
||||
|
||||
With the circuit finalized, I was ready to put everything into a nice case.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
I initially intended to make this lighter a USB charger too because of the 18650 Li-ion battery I was using, but for some reason, the high current draw from the lighter causes the [HT4928S](http://www.hotchip.com.cn/Uploads/goods/2017-11-03/59fc3d69cabf0.pdf) to heat up and release the magic smoke. So for future reference, don’t use it for anything that’s powered directly off the battery.
|
||||
|
||||
After a bunch of sanding and gluing in some magnets, I had a pretty cool lighter on my hands. It’s easily taken apart for debugging/maintenance, has an elegant on switch, and a lid with a nice tactile feel. The one thing I still needed to finalize was the electrode material.
|
||||
|
||||
### Picking an Electrode
|
||||
|
||||
The issue with using an arc is that it’s basically plasma which is quite hot. The tips of the electrodes tend to oxidize over time, making it more difficult to start an arc. They also heat up the electrodes entirely over time which is why I used a fiberglass insert and silicone tubing around the wires.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v1-pogo.jpg" %}
|
||||
|
||||
At first I tried using these Pogo pins I got off eBay because they were probably oxidation resistant. They worked for awhile, but the nice orange arc they created was a telltale sign they wouldn’t last.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v1-copper.jpg" %}
|
||||
|
||||
Next I tried using copper electrodes because GreatScott used them in his arc lighter. It worked really well for quite awhile but they oxidized and failed to create a reliable arc. It seems that whatever circuit I cooked up was more powerful than I thought. (The largest electrode spacing I could reliably use was around 6-7mm which is about 20kV.)
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v1-final-elec.jpg" %}
|
||||
|
||||
The last thing I tried were these tin plated copper wires I got from an arc lighter kit I bought for reference. Looking at commercial arc lighter teardowns, I noticed that they used the same wires but also used a ceramic casing around them. I don’t have the ability to acquire or machine ceramic yet so I stuck to just using the wire. Thankfully, it’s still working to this day.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v1.jpg" %}
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: 3D-Printed Peristaltic Pump
|
||||
date: 2019-06-24
|
||||
categories: projects
|
||||
excerpt: It's the perfect way to transport and rejuvenate etchant while being really easy to manufacture.
|
||||
header:
|
||||
teaser: /assets/img/2019/pump-assembled.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-21-19</sub>
|
||||
|
||||
Of the things that show up while I browse Thingiverse for something cool to print, peristaltic pumps keep coming up. It’s a pretty interesting concept, squeezing a tube to pump a liquid. One of its best features is that liquids only ever come into contact with the tubing so it’s really good for corrosive liquids. Seeing as I needed a pump to help me transport my super corrosive PCB etchant and also to bubble air through it to rejuvenate it, this was perfect.
|
||||
|
||||
My design philosophy focuses on aesthetics and functionality but, especially when it comes to prototypes, ease of manufacture matters too. I like to not cut screws down if I can because I either have to use my Dremel or rethread the ends. I also like to use up the existing parts I have at home because otherwise they’ll just sit there collecting dust. That meant that I couldn’t use any of the designs online, so I modeled my own.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pump-model.jpg" %}
|
||||
|
||||
As expected, the print and assembly process was straightforward. I was quite surprised with how well the pump worked, especially with how little torque it took to drive it. I used a stepper motor because I wasn’t sure what kind of torque the pump needed. Well, based on the design I was pretty sure it was low, but I wasn’t sure how low.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pump-assembled.jpg" %}
|
||||
|
||||
One of the things I needed to do was filter my etchant because I accidentally left a board in too long, like forgot about it and went to sleep long, and everything came off. Ever since then there have been quite a few toner particles in the etchant, but after a couple minutes pumping the etchant through an aquarium filter, it was perfectly clean.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/pump-cleaned-etchant.jpg" %}
|
||||
|
||||
Here’s a video of the pump in action.
|
||||
|
||||
{% include video id="aL9Voegy8xI" provider="youtube" %}
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: Arc Lighter v2
|
||||
date: 2019-06-27
|
||||
categories: projects
|
||||
excerpt: Turned a leftover arc lighter kit into an even smaller version of my first one.
|
||||
header:
|
||||
teaser: /assets/img/2019/arc-v2.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/arc-v2-cad.jpg
|
||||
- image_path: /assets/img/2019/arc-v2-half-assembled.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-22-19</sub>
|
||||
|
||||
After finishing the first lighter, I was still left with the kit I got on Amazon. I decided to make a much tinier arc lighter, preferably one on par with the size of a Zippo. First, I started by shrinking the circuitry as much as I could, while also adding a heatsink. I also replaced the base resistor with a smaller one to increase the voltage output and get the arc to around 3mm.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v2-electronics.jpg" %}
|
||||
|
||||
With the circuitry done, I started designing the lighter. I didn’t have a Zippo on hand at the time, so I worked off of dimensions I found online. I tried to make everything as sleek as possible, complete with a flush switch and magnetic lid.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
After some sanding I was done. Except for having a smaller arc and more heat output, it’s better in every way than v1. It’s even got USB charging.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/arc-v2.jpg" %}
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: MCS-12085 - Mouse Sensor
|
||||
date: 2019-07-04
|
||||
categories: cool-chips
|
||||
excerpt: A pretty cool sensor salvaged from a really old mouse.
|
||||
header:
|
||||
teaser: /assets/img/2019/mcs12085-bottom.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/mcs12085-top.jpg
|
||||
- image_path: /assets/img/2019/mcs12085-bottom.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
During one of my random thoughts, I had the idea of taking apart a mouse and reverse engineering its sensor for use with an Arduino. Rummaging through my dad’s old electronics, I found a PS2 mouse that I was pretty sure we would never use. First I used a PS2 library to communicate with the mouse and make sure it was still working. After that I took it apart to take a look at its sensor.
|
||||
|
||||
As it turns out, this mouse (I think it was a Dell) used a MCS-12085. It’s so old that I couldn’t find any major electronics distributors selling it. Surprisingly though, there are a bunch of articles online about this sensor. I proceeded to desolder this sensor and its associated components to put together my own board.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
While there were libraries available, the communication method shown in [datasheet](http://www.rmrsystems.co.uk/download/MCS12085.pdf) looked pretty simple so I decided it was the perfect exercise to do. I looked at code from this [library](https://github.com/jgrahamc/mcs12085) for inspiration.
|
||||
|
||||
{% highlight C %}
|
||||
#define CLK_PIN 11
|
||||
#define DAT_PIN 12
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
mcs12085_init();
|
||||
}
|
||||
|
||||
int x, y = 0;
|
||||
void loop() {
|
||||
x += mcs12085_dx();
|
||||
y += mcs12085_dy();
|
||||
Serial.print(x);
|
||||
Serial.print(" ");
|
||||
Serial.println(y);
|
||||
delay(5);
|
||||
}
|
||||
|
||||
void mcs12085_init() {
|
||||
pinMode(CLK_PIN, OUTPUT);
|
||||
pinMode(DAT_PIN, OUTPUT);
|
||||
digitalWrite(CLK_PIN, HIGH);
|
||||
digitalWrite(DAT_PIN, LOW);
|
||||
delay(100); //Power Supply Rise Time
|
||||
}
|
||||
|
||||
int8_t mcs12085_dx() {
|
||||
return mcs12085_read_register(0x03);
|
||||
}
|
||||
|
||||
int8_t mcs12085_dy() {
|
||||
return mcs12085_read_register(0x02);
|
||||
}
|
||||
|
||||
uint8_t mcs12085_read_register(uint8_t reg) {
|
||||
mcs12085_write_byte(reg);
|
||||
delayMicroseconds(100);
|
||||
return mcs12085_read_byte();
|
||||
}
|
||||
|
||||
uint8_t mcs12085_read_byte() {
|
||||
pinMode(DAT_PIN, INPUT);
|
||||
uint8_t result = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
digitalWrite(CLK_PIN, LOW);
|
||||
digitalWrite(CLK_PIN, HIGH);
|
||||
result = result << 1 | digitalRead(DAT_PIN);
|
||||
}
|
||||
pinMode(DAT_PIN, OUTPUT);
|
||||
return result;
|
||||
}
|
||||
|
||||
void mcs12085_write_byte(uint8_t val) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
digitalWrite(CLK_PIN, LOW);
|
||||
digitalWrite(DAT_PIN, val & 1 << 7); //get msb
|
||||
val = val << 1;
|
||||
digitalWrite(CLK_PIN, HIGH);
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
The sensor actually worked really well. I’ll probably use it for some kind of robot in the future but for now it’s nice to add another sensor to my repertoire.
|
||||
@@ -0,0 +1,197 @@
|
||||
---
|
||||
title: Wireless Accelerometer
|
||||
date: 2019-07-06
|
||||
categories: projects
|
||||
excerpt: Curiosity as to the identity of a mystery accelerometer taught me about logic analyzers, the ATtiny817, and low level communication with the nRF24l01+.
|
||||
header:
|
||||
teaser: /assets/img/2019/attiny817-accelboard-quarter.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/I2C-Capture.jpg
|
||||
- image_path: /assets/img/2019/I2C-Capture2.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2019/mc3413-breadboard-side-view.jpg
|
||||
- image_path: /assets/img/2019/mc3413-breadboard-top-view.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2019/attiny817-accelboard-kicad-pcb.jpg
|
||||
- image_path: /assets/img/2019/attiny817-accelboard-pcb.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
While taking apart and analyzing the circuit board of a Wii Nunchuk I got off eBay, I tried to figure out what chips they used. The main microcontroller was a glop-top so no luck there. There was also an I2C EEPROM chip which I did end up desoldering and figuring out how to use. However there was one chip that I just couldn’t figure out the part number of. The markings on it didn’t bring up any useful information, but I assumed it was an accelerometer because the Nunchuk has one.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/nunchuk-accelerometer.jpg" %}
|
||||
|
||||
## Figuring Out The Mystery Chip
|
||||
|
||||
Based on the pinout of the EEPROM and following the traces, I deduced that the mystery chip communicated over I2C and was on the same bus as the EEPROM. After connecting an Arduino to the thankfully exposed I2C test points, I ran some [I2C scanner code](https://playground.arduino.cc/Main/I2cScanner/) to figure out the addresses of the devices on the bus. There were two devices, 0x50 matched the EEPROM, so the other, 0x4C, was the accelerometer.
|
||||
|
||||
In hindsight, literally Googling “0x4C accelerometer” would have resulted in finding the datasheet that likely matched the chip. I somehow didn’t think of that at the time because the Nunchuk was so cheap. Well, I did end up learning a lot in the process, so it was a blessing in disguise.
|
||||
|
||||
I started looking into I2C sniffers to analyze all the traffic on the I2C bus. I had a bunch of STM32 Blue Pill boards lying around, so I looked to see if someone had written code for it. To my pleasant surprise, someone [had](https://github.com/kongr45gpen/i2c-sniffer). I loaded the project into STM32CubeIDE and tried it out. First, I tried analyzing the communication between an Arduino and Wii Nunchuk and it worked perfectly. I could see all the addresses, data, start conditions, Acks, etc.
|
||||
|
||||
However, when I tried analyzing the I2C bus that housed the accelerometer, I got a bunch of gibberish. As it turns out, the I2C communication for a Nunchuk runs at 100KHz, but the accelerometer and EEPROM bus runs at 400KHz. Since I didn’t have the money for an expensive logic analyzer, I looked around the internet for an I2C analyzer that could analyze a 400KHz bus but to no avail. Using the I2C scanner code from earlier as a test signal, the fastest bus I could read was about 150KHz.
|
||||
|
||||
### Making a 400KHz I2C Analyzer
|
||||
|
||||
Knowing a bit or two about optimizing and speeding up code, I took a look at the code for the STM32 sniffer. I first tried overclocking the chip from 72MHz to 128MHz by setting the PLL multiplier to 16. To my surprise, this worked flawlessly and I was able to scan buses up to 250KHz. I wondered if I could overclock it even further. I made a new project in STM32CubeIDE to test this out. The maximum PLL setting was 16, which limited me to 128MHz with an 8MHz crystal oscillator, so I replaced the oscillator with a 16MHz one. I immediately ran into stability issues. Dialing the settings back, I was able to overclock the STM32F103C8T6 to 144MHz, a 100% increase!
|
||||
|
||||
Still, I was only able to read buses of up to maybe 300KHz. The next step was optimizing the code. I heard that the HAL library used to control the GPIO adds a lot of overhead, so I looked into that first. It turns out every HAL GPIO access call does a bunch of checks which weren’t necessary in my use case. After a bit of tinkering, I was able to replace all the HAL calls in the code with register calls. I basically just replaced one function, as shown below:
|
||||
|
||||
{% highlight C %}
|
||||
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
|
||||
uint8_t datum;
|
||||
|
||||
if (GPIO_Pin == GPIO_PIN_8) {
|
||||
// Clock triggered; bit received
|
||||
if (GPIOB->IDR & GPIO_PIN_9) {
|
||||
datum = 1;
|
||||
} else {
|
||||
datum = 0;
|
||||
}
|
||||
} else if (GPIOB->IDR & GPIO_PIN_8) { // SDA pin necessarily
|
||||
// START or STOP condition
|
||||
// A for START, B for STOP
|
||||
if (GPIOB->IDR & GPIO_PIN_9) {
|
||||
datum = 'B';
|
||||
} else {
|
||||
datum = 'A';
|
||||
}
|
||||
} else {
|
||||
// Nothing interesting here...
|
||||
return;
|
||||
}
|
||||
|
||||
buffer[bufferPos] = datum; // Store the received bit in the buffer
|
||||
bufferPos++;
|
||||
if (bufferPos >= I2C_BUFFER_SIZE) {
|
||||
bufferPos = 0;
|
||||
}
|
||||
//if (bufferPos == bufferStart) printf("ERROR! I2C buffer too small!\r\n"); // Buffer overflow! //never really happens
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
After that, I was able to read up to around 350KHz I2C buses reliably. The next thing I was going to try was clock stretching, but I found that the decent reliability at 400KHz was good enough for my goal of reverse engineering what commands to send.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
### Revealing Its Identity
|
||||
|
||||
With the commands captured I was finally able to hook up the mystery chip to my Arduino and directly access the raw data off of it. I correctly assumed that it gave out readings in order X, Y, Z, but the data was jumbled at first. Turns out this chip sends out the LSB byte first then MSB byte. A simple bit shift later and everything was working.
|
||||
|
||||
I began to wonder what the chip actually was so I Googled “0x4C accelerometer” and I found some people talking about a MC3451 so I checked that out. Looking through the datasheet, everything seemed to match up perfectly with regards to register locations. The manufacturer, mCube, doesn’t have that many different offerings so I looked at the datasheet of each and found that the model that best matched my mystery chip was the [MC3413](https://mcubemems.com/wp-content/uploads/2014/10/MC3413-Preliminary-Datasheet-APS-048-0029v1.7.pdf). The initialization commands all matched up to reasonable settings on the chip. Still, trying to read the PCODE register returned an incorrect value, so it’s likely my mystery chip isn’t exactly an MC3413 but perhaps a knockoff or reject or something.
|
||||
|
||||
## Figuring Out the ATtiny817
|
||||
|
||||
Initially I was going to use a standard Atmega328P-AU for the microcontroller but I found that it was too big and overkill for my use case. My eyes turned to the ATtiny817 that I had sitting around for over a year. On paper it had every feature I needed so I took out my ATtiny817 Xplained Mini board and fired up Atmel Studio 7. At first I looked through the datasheet and learned how to write directly to the registers controlling I2C and SPI, but I found out that using Atmel START was far easier. The code doesn’t use any unnecessary checks or abstractions, so speed-wise there wouldn’t be much difference.
|
||||
|
||||
### Hooking Up The USART
|
||||
|
||||
Well technically I’m using UART because it’s asynchronous. Atmel START provides USART_0_write() and USART_0_read() for UART communications, but I wanted to get that printf() functionality. Doing some research online, I found that I just needed to add a few lines of code to usart_basic.c. I also wanted to get scanf() working, so using some good old educational guessing I got that working too.
|
||||
|
||||
{% highlight C %}
|
||||
//Add these lines below #if defined(__GNUC__)
|
||||
int USART_0_printCHAR(char character, FILE *stream) {
|
||||
USART_0_write(character);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int USART_0_receiveCHAR(FILE *stream) {
|
||||
return USART_0_read();
|
||||
}
|
||||
|
||||
FILE USART_0_stream = FDEV_SETUP_STREAM(USART_0_printCHAR, USART_0_receiveCHAR, _FDEV_SETUP_RW);
|
||||
|
||||
//Add these lines below #if defined(__GNUC__) in USART_0_init()
|
||||
stdout = &USART_0_stream;
|
||||
stdin = &USART_0_stream;
|
||||
{% endhighlight %}
|
||||
|
||||
### Integrating I2C
|
||||
|
||||
The next step was hooking up the MC3413 to the ATtiny817. I wanted to make sure I could use the MC3413 with a board of my own design, so I desoldered it using my reflow oven and soldered a bunch of tiny wires to the pins.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
Despite how fragile the wiring looks, it actually works for testing.
|
||||
|
||||
Atmel START also provides the i2c_master.h and i2c_simple_master.h libraries for using the I2C interface. It defaults to interrupt mode when you add the module, so make sure you enable interrupts in the SREG register. I was coming from the Wire library for Arduino, so the i2c_simple_master.h library provided a smooth transition.
|
||||
|
||||
I2C generally uses the concept of addresses and registers when communicating. Writing to a register involves sending the device address (with a write bit), the desired register, and finally any data to write. Here’s the code I used to initialize the MC3413:
|
||||
|
||||
{% highlight C %}
|
||||
I2C_0_write1ByteRegister(accel_addr, 0x08, 0x0A);
|
||||
I2C_0_write1ByteRegister(accel_addr, 0x20, 0x35);
|
||||
I2C_0_write1ByteRegister(accel_addr, 0x07, 0x01);
|
||||
{% endhighlight %}
|
||||
|
||||
Reading from registers is similar. First you’d send the device address (with a write bit) and then the desired register. Then you’d send the device address again (with a read bit this time) and then read in as much data as you need. Here’s the code I used to read the accelerometer data from the MC3413:
|
||||
|
||||
{% highlight C %}
|
||||
I2C_0_readDataBlock(accel_addr, 0x0D, dat, 6);
|
||||
{% endhighlight %}
|
||||
|
||||
### Synthesizing SPI and nRF24
|
||||
|
||||
Like with I2C, Atmel START provides a really nice spi\_basic.h library. I chose to use polling mode instead of interrupt mode because I was still a beginner and didn’t need the added functionality of interrupt mode. It also provided an easy transition from the Arduino SPI library. Here’s the 4 methods to be aware of:
|
||||
|
||||
{% highlight C %}
|
||||
uint8_t SPI_0_exchange_byte(uint8_t data);
|
||||
void SPI_0_exchange_block(void *block, uint8_t size);
|
||||
void SPI_0_write_block(void *block, uint8_t size);
|
||||
void SPI_0_read_block(void *block, uint8_t size);
|
||||
{% endhighlight %}
|
||||
|
||||
They’re pretty self explanatory. SPI is generally much simpler than I2C in how to use it. There’s a chip select (CS) pin for each slave. To communicate, just pull the CS pin low and then start sending/receiving data.
|
||||
|
||||
Finding a good nRF24 library for Atmel Studio 7 yielded no results, so I decided to figure out how to port TMRh20’s RF24 library. Looking through the source code, it appears very well written. It uses the Arduino SPI library instead of low level register manipulation so swapping out any SPI calls with spi\_basic.h equivalents would do the job. I ended up only porting over the functionality that I needed, mainly interrupt support and radio settings. My code can be found [here](https://gist.github.com/dragonlock2/0b9706dc3f48a7c6ba9d6c096f0a29ec).
|
||||
|
||||
### Initializing External Interrupts
|
||||
|
||||
One really nice thing about the ATtiny817 is it has interrupt capability on all pins, and supports all interrupt types instead of just pin change like on the Atmega328P. After using Atmel START to setup the pin, I implemented the ISR routine. Every pin belongs to a certain register, so the corresponding interrupt vector gets triggered when the pin’s interrupt triggers.
|
||||
|
||||
{% highlight C %}
|
||||
ISR(PORTB_PORT_vect) {
|
||||
PORTB.INTFLAGS |= 1 << 0; //clear interrupt flag
|
||||
uint8_t tx, fail, rx;
|
||||
RF24_whatHappened(&tx, &fail, &rx);
|
||||
if (tx) { //on success
|
||||
PORTA.OUTSET = 1 << 6; //turn off red
|
||||
PORTA.OUTCLR = 1 << 7; //turn on green
|
||||
} else {
|
||||
PORTA.OUTCLR = 1 << 6; //turn on red
|
||||
PORTA.OUTSET = 1 << 7; //turn off green
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
## Putting It All Together
|
||||
|
||||
With pretty much every component tested and working, it was time to make the PCB. I threw all the components into KiCad, where I did have to make my own schematic symbol for the MC3413. The VLGA-12 footprint it used was available though.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/attiny817-accelboard-schematic.jpg" %}
|
||||
|
||||
As usual the PCB manufacture was pretty easy, but soldering the MC3413 proved quite the pain with my lack of solder paste and the VLGA-12 package. After several reflows and lifted traces, I did get it pinned down. I did accidentally pull up the TX trace so couldn’t use UART. That did allow me to experiment with the UPDI debugging interface, so it worked out.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
||||
|
||||
After putting the board together, I attached a LiPo I salvaged from my old Pebble Steel along with a TP4056 charger board and threw it all into a little 3D printed case. After some meticulous sanding to get all sides smooth and level, I was done.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/attiny817-accelboard-quarter.jpg" %}
|
||||
|
||||
## Demonstration
|
||||
|
||||
One of the things I could never quite figure out, in part due to my relative inexperience at the time and also because I was always encumbered by wires when testing, was how to distinguish between swings and clashes. I literally couldn’t find any code at the time to help me out except for one person who used the Z accelerometer measurement with some thresholds which was still mostly wrong.
|
||||
|
||||
With some basic knowledge of physics and accelerometers, it became pretty clear that a swing provided a low acceleration and a clash a high acceleration. Thus I just needed to combine the X, Y, and Z measurements into a measurement of the gravity vector and just look at that.
|
||||
|
||||
{% include video id="7bsAQNb6qpI" provider="youtube" %}
|
||||
|
||||
As the theory would predict, there’s a pretty clear difference between swings and clashes. I basically facepalmed myself multiple times after this realization because of how much work I put into trying to figure out this simple fact a couple years ago.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/attiny817-accelboard-free-fall.jpg" %}
|
||||
|
||||
Of course, no accelerometer demonstration would be complete without showing the weightlessness experienced in free fall.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: ATtiny817 - Feature Packed Tiny MCU
|
||||
date: 2019-07-07
|
||||
categories: cool-chips
|
||||
excerpt: Atmel's relatively new lineup of chips is quite promising.
|
||||
header:
|
||||
teaser: /assets/img/2019/attiny817-xplained-mini.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
Over a year ago Arrow was giving away free ATtiny817 Xplained Mini boards. Intrigued, I got one along with a few separate ATtiny817’s. I didn’t have much experience moving beyond Arduino at the time, so it was the perfect opportunity to get started using Atmel Studio 7. Reading through the datasheet, which as usual with Atmel was very well written, I was able to get some lights flashing and changed the clock speed. I had to get back to working on Mission Possible for SciOly so I set it aside for the time being.
|
||||
|
||||
Fast forward a year later and I decided to take another look at the ATtiny817. With more experience around my belt, I finally understood what an amazingly cool chip it was. The acquisition of Atmel by Microchip had allowed a bunch of features from PIC chips to be added to AVR chips. Even more, these new chips use the UPDI, a single pin programming interface. It was a very welcome update from the 4-pin ISP programming of older Atmel chips in terms of making PCB design easier.
|
||||
|
||||
Overall, the ATtiny817 provides a perfect balance of features and size for a lot of the uses cases I foresee for my projects. It has 8KB of flash, 512B of RAM, I2C, SPI, USART, 3 Timers, 8 bit DAC, 10 bit ADC, and an internal RC oscillator among other features. Of course, for every project I do I’ll need to analyze what chip provides the best fit, but the ATtiny817 does get a warm welcome into my parts bin.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/attiny817-xplained-mini.jpg" %}
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: LMC6482 - Rail to Rail OpAmp
|
||||
date: 2019-07-08
|
||||
categories: cool-chips
|
||||
excerpt: An excellent opamp for prototyping.
|
||||
header:
|
||||
teaser: /assets/img/2019/lmc6482.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/piezo-amplifier.jpg
|
||||
- image_path: /assets/img/2019/clamp-overview.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
When I was just getting started using opamps late last year, I decided to buy a bunch of the most popular one, LM358. Little did I know about all the various specifications that I had to consider when picking the right opamp. At the time I was trying to amplify the signal for a sine wave encoder I was working on, but I hit a wall when the maximum voltage I could get out of the LM358 was around 3.7v with a 5v power input. It was then that I found out that I had to use a rail to rail opamp. Soon after though I was entering my senior year of high school, complete with a stressful college application season and engineering for upcoming SciOly 2018 competitions and team tryouts, so I set my tinkering aside for the time being.
|
||||
|
||||
Enter EE16B lab section during my 2nd semester at UC Berkeley. We used opamps pretty extensively in that class for various purposes but one of the things I noticed was that we used an [LMC6482](http://www.ti.com/product/LMC6482), which was a rail to rail opamp. We were also forced to order a free sample of them which was pretty awesome. So I spent plenty of time with this cool chip. For me the most important feature is its rail to rail output, but a lot of other specifications, from CMRR to input current are also excellent.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
It is a little expensive though compared to an LM358, but TI’s sample program is pretty generous to students.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Wireless Arduino Nunchuk
|
||||
date: 2019-07-13
|
||||
categories: projects
|
||||
excerpt: Compact, Ergonomic, Functional - behold my Wireless Arduino Nunchuk
|
||||
header:
|
||||
teaser: /assets/img/2019/nunchuk-overall.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/aaa-battery-holder.jpg
|
||||
- image_path: /assets/img/2019/nunchuk-battery.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2019/nunchuk-pcb-after-etch.jpg
|
||||
- image_path: /assets/img/2019/nunchuk-pcb-tin-plated.jpg
|
||||
- image_path: /assets/img/2019/nunchuk-pcb-kapton.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2019/nunchuk-assembly-1.jpg
|
||||
- image_path: /assets/img/2019/nunchuk-assembly-2.jpg
|
||||
- image_path: /assets/img/2019/nunchuk-assembly-3.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
One of the things that I’ve been meaning to build for awhile is an easily programmable and elegant remote to power random wireless projects I come up with. During our RoboSub electrical team meetings I found out that our PM Mark had used a Wii Nunchuk as a remote for his electric skateboard. I thought that was a really awesome idea, so I kept it on the stack of stuff to build the next summer.
|
||||
|
||||
My design philosophy is pretty much the embodiment of “form follows function.” No one is going to use a product that doesn’t work. However, people also like to use products that have a nice aesthetic. The two aren’t mutually exclusive. Usability is also very important because if a product is hard to use, fewer people will use it. That’s why I chose a Wii Nunchuk as the outer shell for my remote. It packs excellent ergonomics, ease of use, and multiple control inputs all into a compact and beautiful package.
|
||||
|
||||
## Build
|
||||
|
||||
At first I wanted to 3D print a Nunchuk and then fit everything inside but my CAD skills aren’t yet advanced enough to make organic looking designs so I ended up buying a cheap Nunchuk off Ebay. Upon taking it apart I immediately began to wonder how I was going to fit a battery inside it. I didn’t have any LiPo batteries small enough so I picked a NiMH AAA as a good compromise of size and capacity as well as being swappable. Then I started removing internal structures to make room for the AAA and cut a hole large enough to fit. Using some springs from the Amazon Dash Button and a 3D printed part, I fitted the Nunchuk with a battery holder, leaving ample room for rest of the electronics.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
The next step was picking out the features of the remote. I chose to keep the joystick and buttons, but added an MPU6050 and RGB LED. Since I still have a couple left and it’s the most widely supported Arduino microcontroller, I stuck with the Atmega328P-AU. I threw everything into KiCad, where the only part I had to design a schematic symbol and footprint for was the generic joystick.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/nunchuk-schematic.png" %}
|
||||
|
||||
With my trusty caliper in hand and a couple 3D printed tests, I was able to create a board outline that would fit perfectly into the Nunchuk and maximized available area for placing components.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/nunchuk-kicad-pcb.jpg" %}
|
||||
|
||||
Making the PCB was relatively straightforward, except for the drilling because I didn’t have the right size bits. FR4 does sand nicely, so getting it exactly to size was a breeze.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
I even tried making a solder mask using Kapton tape and an X-Acto knife. It worked really well, but is too tedious for me to give a recommendation. The UV cure solder mask I’m working on now is far easier and more effective.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/nunchuk-pcb-half-assembled.jpg" %}
|
||||
|
||||
I had to jerry-rig my soldering job because I didn’t have solder paste, but after several reflows, lots of flux, and some of the finest trace repairs I’ve ever done, the board was working.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
||||
|
||||
All the electronics ended up fitting really nicely. I had to add a piece to improve the rigidity of the casing but other than that, the rest of the assembly was clean. I really liked how I routed the RGB LED to the front top and made a custom port at the bottom for programming and serial debugging. There was also quite a bit of room underneath the PCB to fit the nRF24 board.
|
||||
|
||||
## Programming
|
||||
|
||||
Since I didn’t have a specific project I wanted to control with this remote, I stuck to creating a template that would make integrating it into any project relatively easy. You can find it [here](https://gist.github.com/dragonlock2/6c691bc0acdd57ce0ce8567d596a251c). The Teensy I used as a receiver has a little quirk because it uses ARM instead of AVR architecture.
|
||||
|
||||
Using a Teensy with the FreqCount library, I first calibrated the internal RC oscillator, setting the OSCCAL register.
|
||||
|
||||
Sending the battery voltage in a uint8_t (range is 0-255, which is good enough for a 1.5v AAA) instead of as a float proved a little tricky, but I got it working. analogRead() returns an int which when multiplied by 330 overflows so I had to cast it as a long.
|
||||
|
||||
The code for the joystick was a little trickier than expected to get clean because it centers at a point not exactly in the middle of its range so I had to use two different map calls.
|
||||
|
||||
The last part was calibrating the MPU6050 and getting it working with the InvenSense Teapot Demo. I used a program I found online to get the gyroscope and accelerometer offsets, using some tape rolls to hold the remote in the proper orientation. I then set the fine gain on the accelerometer so that the measured gravity vector was constant in all orientations.
|
||||
|
||||
Since I was heading to Maui soon, I didn’t have much time to do much other than create a really basic demonstration of using the remote as a mouse. I pretty much just used the raw gyroscope values to determine movement and the accelerometer to determine orientation to simulate lifting the mouse.
|
||||
|
||||
{% include video id="xuXBSWcld10" provider="youtube" %}
|
||||
|
||||
Overall, as a prototype I’d say this was a success. I’m not sure what the market is for these things but it’d be pretty cool to bring this into production. The case needs rework and I’d probably want to modify the internals to incorporate the nRF24 into the main board and use a 9-axis IMU and add USB programming/debugging but otherwise it’s pretty solid.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Tinkering with TensorFlow (and OpenCV)
|
||||
date: 2019-07-27
|
||||
categories: projects
|
||||
excerpt: Some free time and a project idea provided the perfect opportunity to get my feet wet with TensorFlow and OpenCV.
|
||||
header:
|
||||
teaser: /assets/img/2019/tf-imgs.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-19-19</sub>
|
||||
|
||||
One project I’ve really wanted to do for a long time is a robot that identifies lightsabers and shoots at them using Nerf darts or something. Star Wars was a huge part of my childhood and recreating those iconic blaster deflection scenes would be so cool. I keep getting distracted by other cool things I want to do so hopefully I’ll get around to it next summer or so.
|
||||
|
||||
During my family trip to Maui this summer, I had a bunch of free time on my hands whenever we weren’t hiking, eating, or going to the beach. I thought about how I could be productive and settled on figuring out the vision system for my future robot.
|
||||
|
||||
A few weeks previously, I experimented with OpenCV on my Raspberry Pi 2 and tried using filters and other functions to draw a box around any identified lightsabers. It worked rather well, but it was prone to several inaccuracies that I couldn’t quite adjust out. It also didn’t work for flashlight sabers (like SaberForge), whose brightness decrease significantly by the end of the blade.
|
||||
|
||||
{% include video id="Uj7dXJSIqWI" provider="youtube" %}
|
||||
|
||||
My code also only worked for one lightsaber and didn’t identify any colors, so I decided that it was the perfect time to dive into using machine learning and object detectors to do what I wanted. I learned about using object detectors with OpenCV’s DNN module so I looked up how to train my own model. I decided on using TensorFlow because it’s really popular and there’s a lot of tutorials out there on it.
|
||||
|
||||
### Training a Model
|
||||
|
||||
If you want step by step instructions on how to train your own object detector using TensorFlow, check out some of the links below, especially the Medium articles. I will just be outlining the basic steps I took and fixes for some hiccups along the way.
|
||||
|
||||
The first part is choosing a model. Since I’m going to end up deploying my model on relatively weak hardware (probably a Jetson Nano when I eventually buy one) and lightsabers are pretty easy to describe and supposedly detect, I chose to use SSDLite MobileNet v2.
|
||||
|
||||
The next step is creating a dataset, which takes up a surprising amount of time. I spent quite awhile pouring through Google Images, picking out images of varying color, background, orientation, brightness. Since I wanted the model to work with flashlight lightsabers and not just LED blade ones, I made sure to also include a bunch of cosplay pictures.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/tf-imgs.jpg" %}
|
||||
|
||||
After getting a bunch of images (all PNGs and JPEGs), I had to manually annotate each one. I used [LabelImg](https://github.com/tzutalin/labelImg) because it seemed to be the most popular and was quite easy to use. I put all of the generated XML files into a separate folder.
|
||||
|
||||
TensorFlow requires data to be in the TFRecord file format. There’s scripts that automatically convert the files from LabelImg to TFRecord, but I had issues with it and ended up writing my own script based off of the example on the Github. You can find it [here](https://gist.github.com/dragonlock2/f2486feb42fa211708a53ec85e45a74b).
|
||||
|
||||
Finally, I got onto doing the actual training. A lot of tutorials say to use train.py but the new way is using model_main.py. The [Github](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_locally.md) provides pretty clear directions on this. I then used TensorBoard to monitor everything.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/tensorboard-light.jpg" %}
|
||||
|
||||
Since I was in Maui and didn’t have access to my GTX 970 powered desktop at home, I had to do all the training on my Macbook Pro. After 3 days of my Macbook running at basically 100% load, the training finally got to a point where I was happy with the accuracy. One thing to keep in mind is not to leave the training going on too long because you might end up overfitting.
|
||||
|
||||
Next is exporting the model as a .pb file for OpenCV. OpenCV also requires a .pbtxt to import which can be generated using tf_text_graph_ssd.py on the OpenCV repository. I ran into an AssertionError when running it, which I found a fix for [here](https://github.com/opencv/opencv/issues/11560). Turns out the nodes need to be sorted.
|
||||
|
||||
After all that, I was finally able to import my model into OpenCV and run it.
|
||||
|
||||
{% include video id="xziQ5t213Mg" provider="youtube" %}
|
||||
|
||||
The results are actually pretty good. My training data could have been improved to include multiple lightsabers hitting each other, but it did a great job nevertheless. One thing to note is that the video is slowed down because my Macbook isn’t quite fast enough to run the detector at the playback speed. Still, I’d call this a success.
|
||||
|
||||
Using TensorFlow to train and deploy models is pretty cool, but it doesn’t really teach you anything about how machine learning and object detectors actually work. I’m familiar with the basics, but I’m definitely going to take machine learning classes later because I really want to gain a fundamental understanding of how it works.
|
||||
|
||||
#### Sources:
|
||||
|
||||
- <https://medium.com/@WuStangDan/step-by-step-tensorflow-object-detection-api-tutorial-part-1-selecting-a-model-a02b6aabe39e>
|
||||
- <https://towardsdatascience.com/creating-your-own-object-detector-ad69dda69c85>
|
||||
- <https://towardsdatascience.com/how-to-train-your-own-object-detector-with-tensorflows-object-detector-api-bec72ecfe1d9>
|
||||
- <https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md>
|
||||
- <https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_locally.md>
|
||||
- <https://jeanvitor.com/tensorflow-object-detecion-opencv/>
|
||||
- <https://github.com/opencv/opencv/issues/11560>
|
||||
- <https://www.pyimagesearch.com/2017/09/18/real-time-object-detection-with-deep-learning-and-opencv/>
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: DIY DC Current Clamp
|
||||
date: 2019-08-09
|
||||
categories: projects school
|
||||
excerpt: In a relatively straightforward build, I learned how DC current clamp meters work and ended up with a surprisingly functional device.
|
||||
header:
|
||||
teaser: /assets/img/2019/clamp-overview.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/current-measurement-setup.jpg
|
||||
- image_path: /assets/img/2019/current-measurement-data.jpg
|
||||
---
|
||||
|
||||
<sub>Written 8-18-19</sub>
|
||||
|
||||
While competing at RoboSub this year (our 1st year competing!), one of the things we realized was that we likely seriously overestimated the current demands of our sub. At competition we realized that, even after a whole day, we barely depleted one of our 4 batteries. Thus, we decided that one of the things we needed to do next year was determine power draw with actual current measurements. Total current draw will probably still be in the 10s of amps, and since we definitely don’t want to deal with using current shunts in the tight electronics enclosure of our sub, I wondered about using those current clamps that I’ve heard about.
|
||||
|
||||
As it turns out, DC current clamp meters are widely available on Amazon. As usual, I began to wonder how they worked since DC current doesn’t generate a changing magnetic field. A quick Google search later and I found out that these meters cleverly use a Hall effect sensor to measure the strength of the magnetic field. Thus, I decided to put together a little prototype.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/currentclamp-drawing.jpg" %}
|
||||
|
||||
The basic composition of a DC current clamp is a ferrite toroid, cut in half to fit a wire through, and a linear Hall effect sensor. The toroid “captures” the magnetic field from the wire and directs it straight into the sensor. It’s a pretty neat way of noninvasive current measurement.
|
||||
|
||||
## Build
|
||||
|
||||
Before doing any CAD or finalizing any design, I needed to make sure the theory actually worked. I took the largest ferrite toroid I had and chopped it in half with a hacksaw. I soldered some wires onto a 49E linear Hall effect sensor and hooked it up to an Arduino outputting the analog readings to the serial monitor graph. One hand holding all the parts around the battery wire of my RC car, other hand on the remote, and eyes on the screen, I was delighted to see the readings change as I revved the RC car. (I also realized how much noise a brushless motor puts into the system, something we’ll consider more in-depth next year for the sub.)
|
||||
|
||||
Next, I modeled a basic clamp in Fusion 360. I still had to determine the amplifier requirements and whether to use an example circuit I found online or one of my own design. I printed out my design and proceeded with prototyping the circuit.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/clamp-proto.jpg" %}
|
||||
|
||||
To improve usability and reliability, I ended up designing my own circuit based off of one I found [online](http://www.electronoobs.com/eng_circuitos_tut12_1.php). I made the gain adjustable as well as the reference voltage on the non-inverting input of the opamp. I added resistors around the potentiometer to make finely adjusting it easier. Since making PCBs is pretty easy now, I threw the circuit into KiCad.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/clamp-kicad.jpg" %}
|
||||
|
||||
I did my best to design the circuit to reduce noise because we are making pretty fine measurements.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/clamp-pcb-soldermask.jpg" %}
|
||||
|
||||
I also tried doing a DIY soldermask for the second time and it actually worked pretty well.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/clamp-cad.jpg" %}
|
||||
|
||||
I updated the clamp design with the observations I made while using the prototype and also made room for the circuit board.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/clamp-overview.jpg" %}
|
||||
|
||||
## Calibration & Code
|
||||
|
||||
First things first, I adjusted the potentiometers so that the range of the meter was around 100A. I jacked up the gain to accentuate small voltage differences to make it easier to adjust the reference voltage on the non-inverting input of the opamp. After setting the reference voltage so that the output is roughly around 2.5v with no wire clamped, I used my battery charger to provide a 5A reference current to help adjust the gain.
|
||||
|
||||
Next thing to do was to gather a bunch of data points. I didn’t have any accurate current sources so I jerry-rigged a setup with a current shunt, some lithium batteries, and a power resistor. I made sure to be as safe as possible, but I still wouldn’t recommend doing what I did for safety reasons.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
After all that was done, I wrote a simple Arduino program to put everything together.
|
||||
|
||||
{% highlight C++ %}
|
||||
#define BUFFER_SIZE 512
|
||||
#define READ_PIN A0
|
||||
|
||||
#define CENTER 503.2 //reading at 0A
|
||||
#define SLOPE 0.1625 // in Amps/Reading
|
||||
/*
|
||||
* To Perform Reading:
|
||||
* 1. Probs wanna rough calibrate against known current
|
||||
* 2. Angle changes CENTER for some reason, so hold at angle u wanna measure
|
||||
* and update CENTER
|
||||
* 3. Do the reading.
|
||||
*/
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
float r = getReading();
|
||||
Serial.print("Raw: ");
|
||||
Serial.print(r);
|
||||
Serial.print("\t Current(A): ");
|
||||
Serial.println((r - CENTER) * SLOPE);
|
||||
delay(1);
|
||||
}
|
||||
|
||||
float getReading() {
|
||||
long tot = 0;
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
tot += analogRead(READ_PIN);
|
||||
}
|
||||
return 1.0 * tot / BUFFER_SIZE;
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
At the end of the day, I ended up with a decently accurate (±0.5A) device. It definitely won’t replace one I can buy off Amazon but for a total cost of around $1, it’ll do the job just fine for now. And it was an awesome learning experience.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Hello World!
|
||||
date: 2019-08-16
|
||||
categories: other
|
||||
excerpt: Hello there! Obligatory first post.
|
||||
header:
|
||||
teaser: /assets/img/2019/maui-valley-me.jpg
|
||||
---
|
||||
|
||||
{% include figure image_path="/assets/img/2019/maui-valley-me.jpg" %}
|
||||
|
||||
Hi! I’m Matthew Tran and I’ve been a maker for as long as I can remember. I’m currently an EECS major at UC Berkeley, pursuing my passion for all things electrical engineering and computer science. After years of tinkering and building things, I’ve finally decided to start a blog to document most of what I’ve done and perhaps be a stepping off point to inspire and help others. I’m endlessly fascinated by what I do and would love to share that with everyone.
|
||||
|
||||
Every post before this one will be my attempt at retracing all the knowledge I’ve gained (and fun I’ve had :P) over the past couple years. It feels a bit weird to write things from the perspective of my younger self, so I’ll write about everything with the perspective of hindsight. For dates, I’m using rough approximations instead of today’s date because it’ll be pretty cool to scroll back and say, “Hey! I did that way back then!”
|
||||
@@ -0,0 +1,601 @@
|
||||
---
|
||||
title: Setting Up A WordPress Server
|
||||
date: 2019-08-18
|
||||
categories: how-to projects
|
||||
excerpt: Just about everything you need to know about setting up a WordPress server yourself.
|
||||
header:
|
||||
teaser: /assets/img/2019/wp_test.jpg
|
||||
---
|
||||
|
||||
Getting started with hosting your own website has really never been easier. However, while getting started is just a few commands away, getting something like WordPress up and running at full functionality can seem like a daunting task. When I set up my web server, I came across so many little hiccups and it felt like there was always something else to consider. Security was my main concern, but so was speed and SEO optimization. After all that I could finally focus on making content. This post will be my attempt at a comprehensive how-to combining all the things I’ve learned along the way on setting up my very own WordPress site from scratch.
|
||||
|
||||
## Getting Your Own Domain
|
||||
|
||||
There’s really no shortage of different domain registrars out there to buy a domain from, but in the end I decided to use [Google Domains](https://domains.google) because it has the cleanest user experience, very competitive pricing, and doesn’t try to sell you anything more than just a domain. For someone who is just starting out making a website and doesn’t need any extra functionality, it’s an excellent choice. As of the time of writing, I own both [matthewlamtran.com](https://matthewlamtran.com) and [matthewtran.dev](https://matthewtran.dev) and it only costs $12 a year for each domain. I’m sure there are better choices out there, but this is the one I’m sticking with. Also, make sure you turn on auto-pay or remember to pay the annual fee because your domain can go up for sale again, where it will likely get picked up by someone else.
|
||||
|
||||
If you don’t want to buy a domain yet, there are free DNS services out there, like [dynu.com](https://www.dynu.com), where you can get a free domain that looks something like `<your name>.<free dns provider>.com`. I don’t use it anymore, but to each their own.
|
||||
|
||||
## Self-Hosting vs. Using a Hosting Service
|
||||
|
||||
When I first bought [matthewlamtran.com](https://matthewlamtran.com) over a year ago, I had to choose between hosting a website myself or using one of a seemingly limitless number of hosting services. Since I was just getting started with making my own website and didn’t expect very much traffic, if any at all, I decided to host my website on a Raspberry Pi at home. Some of the benefits of hosting at home are total control over your own server, lower costs, and a greater understanding of the internal workings of a web server. However, it also makes you responsible for the security and maintenance and there’s no customer support to call if something goes wrong.
|
||||
|
||||
However, as my website grows, I will likely switch over to a hosting service because I will become limited by the internet speeds available at my home (I’m looking at you Charter Spectrum with those low upload speeds) and also by the scalability of my server. Electricity costs also become a concern as server load increases. For now and for the foreseeable future though, I’m sticking to running my own server. If you want to use a hosting service, you should still continue reading because you can still learn a thing or two from my experience.
|
||||
|
||||
## Redirecting a Domain Name to Your Own IP Address
|
||||
|
||||
Underneath the pretty website names you type into your browser is the Domain Name Service (DNS). It matches every domain name to an IP address, which in turn locates the web server in cyberspace. There’s also two different address types: IPv4 and IPv6. IPv6 is needed because, in our growing digital age, we will eventually run out of IPv4 addresses. For now, I’ll just cover IPv4, but the instructions should be applicable to IPv6 if necessary.
|
||||
|
||||
First, go to a website like [whatismyip.com](https://www.whatismyip.com) while on a device using the same internet that your server will be on. Record the IP address.
|
||||
|
||||
Now login to the domain registrar where you bought your domain, or the free DNS service if you’re using that. I’m using Google Domains, but these instructions should be applicable to other registrars.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/googledomains_1.jpg" %}
|
||||
|
||||
Click on “Manage”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/googledomains_2.jpg" %}
|
||||
|
||||
Click on “DNS”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/googledomains_3.jpg" %}
|
||||
|
||||
Finally scroll all the way down and add two entries to “Custom resource records.” Replace the IP address with your own recorded IP address.
|
||||
|
||||
DNS records take some time to update, around 20 minutes for Google Domains. To check if the DNS is updated, try the following command in your terminal (“-n” instead of “-c” for Windows):
|
||||
|
||||
{% highlight bash %}
|
||||
ping -c 5 <domain name>
|
||||
{% endhighlight %}
|
||||
|
||||
If it all works out, you should see your IP address from earlier!
|
||||
|
||||
If your Internet Service Provider (ISP) doesn’t have a static IP option, which is pretty much most residential internet plans, then this IP address will change every once in a while, in which case you’ll need to repeat the redirect process again. Pretty much the only ISP available in my area is Charter Spectrum, whose business and static IP options are unreasonably expensive for now, so I’m sticking to manually updating the DNS records for now. Luckily, my IP address pretty much only changes once a year, so I’m good. If you want to look into it, there’s dynamic DNS services out there that will do the updates and stuff for you. Now onto the actual web server setup part!
|
||||
|
||||
## What You’ll Need
|
||||
|
||||
- computer that you can leave on 24/7 (I recommend a Raspberry Pi if you’re just starting out)
|
||||
- microSD card or USB flash drive for installing the OS
|
||||
- an ethernet cable (I don’t recommend using WiFi because of reliability and speed)
|
||||
|
||||
## Installing the OS
|
||||
|
||||
Linux powers a surprisingly large part of the internet. Generally speaking, it’s faster, more secure, and ultimately easier to use. Even more, most distros are completely free and open-source. It was a pretty easy choice going with Linux, but since I am admittedly still pretty new to the world of Linux distros, I picked the most familiar distro, Ubuntu Server. They also have a version for the Raspberry Pi, so if you ever want to upgrade your server to a full size PC or something, it should be a relatively simple transition.
|
||||
|
||||
The process of installing an OS on the Raspberry Pi has been done to death, but it basically involves downloading the OS from the website, flashing it to a microSD card using something like [Etcher](https://www.balena.io/etcher/), and then finally plugging it into the Pi.
|
||||
|
||||
Installing Ubuntu Server onto a normal PC is pretty similar, but instead flashing to a flash drive, plugging into the computer, and going through the setup process.
|
||||
|
||||
## Initial Setup
|
||||
|
||||
Now plug in your server and connect the ethernet cable from your server to your router. It’s possible to then figure out the IP using nmap (`sudo nmap -sP 192.168.0.1/24` on MacOS) to SSH into it, but out of convenience I just hooked up a monitor and keyboard to my server and logged in with the default username and password (both “ubuntu”).
|
||||
|
||||
I cannot stress enough the importance of having a strong password for everything, especially when setting up a web server. To change your password, run the following:
|
||||
|
||||
{% highlight bash %}
|
||||
passwd
|
||||
{% endhighlight %}
|
||||
|
||||
Now we need to update everything so run:
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt update && sudo apt upgrade
|
||||
{% endhighlight %}
|
||||
|
||||
`update` updates the list of packages and their versions while `upgrade` does the actual upgrading of the packages to their latest versions. Ubuntu Server does this update process automatically, but it doesn’t hurt to check every once in awhile.
|
||||
|
||||
SSH should be enabled by default, but just in case, run:
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install openssh-server
|
||||
{% endhighlight %}
|
||||
|
||||
I usually install a text editor at this point, which in my case is vim. You can use nano, but I think it’s worth it to learn vim.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install vim
|
||||
{% endhighlight %}
|
||||
|
||||
For future reference, I tend to run a lot of commands from the home directory. I will assume you have basic knowledge of vim. All you really need to know is press esc then type `wq` to save and quit and also press i to enter typing mode. I’ll also assume you know how to use `cd` to change directories and all that. The basics of getting around a terminal really.
|
||||
|
||||
### Setting Up a Static IP
|
||||
|
||||
Just like how your external IP address changes, every device on your local network’s internal IP address can change too. First, we’ll setup a static IP on your server. Run the following:
|
||||
|
||||
{% highlight bash %}
|
||||
cd /etc/netplan
|
||||
ls
|
||||
{% endhighlight %}
|
||||
|
||||
There should be a .yaml file in the directory. We’re going to edit it.
|
||||
|
||||
{% highlight bash %}
|
||||
vim <file name>.yaml
|
||||
{% endhighlight %}
|
||||
|
||||
Change it from something that looks like this:
|
||||
|
||||
{% highlight yaml %}
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
dhcp4: false
|
||||
{% endhighlight %}
|
||||
|
||||
To something like this:
|
||||
|
||||
{% highlight yaml %}
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
dhcp4: false
|
||||
addresses: [192.168.0.***/24]
|
||||
gateway4: 192.168.0.1
|
||||
nameservers:
|
||||
addresses: [8.8.8.8,8.8.4.4]
|
||||
{% endhighlight %}
|
||||
|
||||
Keep in mind that what you put under “addresses” and “gateway4” can change slightly depending on your network, but running “ifconfig” can help you figure out what the right values are. The last 3 digits of the address in “addresses” (the \*\*\*), can be pretty much whatever you want up to 255. For more details check [here](https://linuxconfig.org/how-to-configure-static-ip-address-on-ubuntu-18-04-bionic-beaver-linux). Now run:
|
||||
|
||||
{% highlight bash %}
|
||||
sudo netplan apply
|
||||
{% endhighlight %}
|
||||
|
||||
Now we need to head over to your router’s admin page, usually located at something like 192.168.0.1. Every router is a little different but the gist is that we’re going to assign the MAC address of our server’s ethernet to the specific IP address we listed under “addresses,” without the /24.
|
||||
|
||||
At this point I like to run a good old `sudo reboot`, disconnect the monitor and keyboard, and head over to my laptop to complete the rest of the setup process. Congrats, your server is headless! To login to your server you can SSH into it (you might need to install a SSH client like Putty if you’re on Windows).
|
||||
|
||||
{% highlight bash %}
|
||||
ssh ubuntu@<ip address>
|
||||
{% endhighlight %}
|
||||
|
||||
## Installing the LEMP Stack
|
||||
|
||||
The LEMP stack consists of Linux, Nginx, MySQL, and PHP. It’s the backbone of pretty much any web server. You’ll hear a lot about LAMP, which is similar, but uses Apache instead of Nginx. For it’s speed and recency, I’ve found Nginx to be a better choice for my use case and this tutorial will reflect that. One thing to keep in mind about Nginx is that most tutorials out there assume you use Apache, so just add Nginx to your search query and you’ll be good.
|
||||
|
||||
### Installing Nginx
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install nginx
|
||||
{% endhighlight %}
|
||||
|
||||
Now we need to allow Nginx through the firewall.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo ufw allow 'Nginx Full'
|
||||
sudo ufw enable #this might warn about disrupting ssh, but it's fine
|
||||
sudo ufw status
|
||||
{% endhighlight %}
|
||||
|
||||
You’ll probably want Nginx to start up whenever your server restarts.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo systemctl enable nginx
|
||||
{% endhighlight %}
|
||||
|
||||
{% include figure image_path="/assets/img/2019/nginx_default.jpg" %}
|
||||
|
||||
Now’s a good time to test your Nginx installation. Head over to a web browser and type in your server’s IP address (the one you use for SSH). If you see something like the right image, you’re good!
|
||||
|
||||
Now’s also a good time to port forward web requests from the outside world to your server. If you’ve ever setup a Minecraft server, then this should be easy. If not, Googling your router’s model plus “port forward” should lead you in the right direction. You’ll want to forward requests from port 80 (HTTP) and port 443 (HTTPS) to your server’s internal IP address. When that’s done try typing in your domain name to your browser and you should see the same page as before!
|
||||
|
||||
#### Setting Up Server Blocks
|
||||
|
||||
Currently Nginx is routing every request to the default server block, whose files are stored in `/var/www/html`. However it’s good practice to setup a server block for each website you’re hosting (even if it’s only one). For example, the files for my server are stored in `/var/www/matthewtran.dev/html`. What’s super cool is that this allows you to host multiple websites on the same server (although you might not want to for security reasons, but it depends). To make things easier to read I’m going to use **matthewtran.dev** as the domain for my examples, but be sure to change **matthewtran.dev** to whatever your domain is when you do this.
|
||||
|
||||
First, Nginx uses the user “www-data” when interacting with the files on your website. In order to avoid file permission errors later with WordPress, we’ll add “ubuntu” to the same user group as “www-data”.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo usermod -a -G www-data ubuntu
|
||||
{% endhighlight %}
|
||||
|
||||
Now let’s make the appropriate directories and assign proper permissions.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo mkdir -p /var/www/matthewtran.dev/html
|
||||
sudo chown -R $USER:www-data /var/www/matthewtran.dev/html
|
||||
{% endhighlight %}
|
||||
|
||||
I generally like to remove the default html folder at this point.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo rm -r /var/www/html
|
||||
{% endhighlight %}
|
||||
|
||||
For testing let’s create a file named index.html inside this new html folder. You can pretty much put any old html code. Here’s literally what I put:
|
||||
|
||||
{% highlight bash %}
|
||||
Welcome to matthewtran.dev!
|
||||
{% endhighlight %}
|
||||
|
||||
Now we need to tell Nginx about this new server block we made.
|
||||
|
||||
{% highlight bash %}
|
||||
cd /etc/nginx/sites-available
|
||||
{% endhighlight %}
|
||||
|
||||
Add the following to a new file named “matthewtran.dev”
|
||||
|
||||
{% highlight bash %}
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
root /var/www/matthewtran.dev/html;
|
||||
index index.php index.html index.htm;
|
||||
|
||||
server_name matthewtran.dev www.matthewtran.dev;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?q=$uri&$args;
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Now we link this new config file to the sites-enabled folder.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo ln -s /etc/nginx/sites-available/matthewtran.dev /etc/nginx/sites-enabled/
|
||||
{% endhighlight %}
|
||||
|
||||
I also like to unlink the default enabled site at this point.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo rm /etc/nginx/sites-enabled/default
|
||||
{% endhighlight %}
|
||||
|
||||
If you’re going to add more sites, it’s also a good idea to uncomment this line in /etc/nginx/nginx.conf.
|
||||
|
||||
{% highlight bash %}
|
||||
server_names_hash_bucket_size 64; #uncomment this one
|
||||
{% endhighlight %}
|
||||
|
||||
Finally, we need to check our config files and restart (reload works too) Nginx to apply these updates.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo nginx -t
|
||||
sudo systemctl restart nginx
|
||||
{% endhighlight %}
|
||||
|
||||
#### Enabling HTTPS!
|
||||
|
||||
Enabling HTTPS on your website is an incredibly important step. Luckily, it’s pretty easy to do (and free).
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install python-certbot-nginx
|
||||
sudo certbot --nginx -d matthewtran.dev -d www.matthewtran.dev
|
||||
{% endhighlight %}
|
||||
|
||||
Again, make sure to change **matthewtran.dev** to your domain name. You’ll need to enter your email, which Let’s Encrypt uses to tell you if your certificates are about to expire, in which case you’ll need to use a good old `sudo certbot renew`. We can also make that happen automatically.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo crontab -e
|
||||
{% endhighlight %}
|
||||
|
||||
Add this line to the bottom.
|
||||
|
||||
{% highlight bash %}
|
||||
0 12 * * * /usr/bin/certbot renew --quiet
|
||||
{% endhighlight %}
|
||||
|
||||
Now head over to your web browser and head to your domain. You should see that it’s now secured using HTTPS!
|
||||
|
||||
### Installing MySQL
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install mysql-server
|
||||
sudo mysql_secure_installation
|
||||
{% endhighlight %}
|
||||
|
||||
You’ll be asked to set a new set a password for a root user. There’s a bunch of questions after that I just said yes to because it’ll increase security. Now we need to allow logging in to the root user using a password.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo mysql
|
||||
{% endhighlight %}
|
||||
|
||||
{% highlight sql %}
|
||||
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '<your password>';
|
||||
mysql> FLUSH PRIVILEGES;
|
||||
mysql> exit
|
||||
{% endhighlight %}
|
||||
|
||||
Now if you try `sudo mysql`, it should deny you, but try `mysql -u root -p` and you should be able to login.
|
||||
|
||||
Aside: After all this setup, I’ve realized that I didn’t actually need to enable logging into root and disabling logging into root from phpMyAdmin later. Using SQL commands, I can grant the appropriate permissions for the user that has access to my WordPress database as well as not giving all the permissions to the phpMyAdmin user. This way, even if someone hacks into my phpMyAdmin, they can’t login into any account that has a dangerous level of permissions. You know, I’ll go do that right now.
|
||||
|
||||
### Installing PHP
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install php-fpm php-mysql
|
||||
{% endhighlight %}
|
||||
|
||||
Now we need to tell Nginx to use PHP, so head over to your website config file in `/etc/nginx/sites-available`. Update it to look something like this (you’ll notice a bunch of extra lines that relate to HTTPS but don’t worry about those):
|
||||
|
||||
{% highlight bash %}
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
root /var/www/matthewtran.dev/html;
|
||||
index index.php index.html index.htm;
|
||||
|
||||
server_name matthewtran.dev www.matthewtran.dev;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?q=$uri&$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Like before, restart nginx using `sudo nginx -t` and `sudo systemctl restart nginx`.
|
||||
|
||||
Now head over to your website’s html folder (the one with index.html in it) and make a new file called info.php and write this in it:
|
||||
|
||||
{% highlight php %}
|
||||
<?php
|
||||
phpinfo();
|
||||
{% endhighlight %}
|
||||
|
||||
Now if you go to `<your domain name>/info.php`, you should see a page that looks like this:
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpinfo.jpg" %}
|
||||
|
||||
If everything’s working, it’s a good idea to remove info.php because it tells a would be hacker a lot about your website, which could be used to exploit security vulnerabilities.
|
||||
|
||||
Congrats! You’ve installed a full LEMP stack!
|
||||
|
||||
## Installing phpMyAdmin
|
||||
|
||||
{% highlight bash %}
|
||||
sudo apt install phpmyadmin
|
||||
{% endhighlight %}
|
||||
|
||||
The installation will tell you to choose between Apache and something else, but just press tab then enter to skip that. You also need to make a password for an account with username “phpmyadmin”. Use default settings for any other questions.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo ln -s /usr/share/phpmyadmin /var/www/matthewtran.dev/html/<str>
|
||||
{% endhighlight %}
|
||||
|
||||
A lot of bots and hackers like to scan websites to see if phpMyAdmin is installed, after which they may perform a brute force attack on the login. Let’s make it harder for them by putting something random in place of `<str>`. Some people (i.e. younger me) tend to just put “phpMyAdmin”. Don’t do that.
|
||||
|
||||
Now head over to `matthewtran.dev/<str>`, again replacing **matthewtran.dev** with your domain name, and you should see the phpMyAdmin login page like below:
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin.jpg" %}
|
||||
|
||||
Try logging into the users root or phpmyadmin with the passwords you set to see if everything’s working.
|
||||
|
||||
Now log into the root account (or the phpmyadmin account if you grant proper permissions to it through MySQL or the root account in phpMyAdmin) because we need to create a database for WordPress.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin_1.jpg" %}
|
||||
|
||||
Click on “New”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin_2.jpg" %}
|
||||
|
||||
Enter a name for your WordPress database, like “wpdata”, and press “Create”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin_3.jpg" %}
|
||||
|
||||
Click on “Privileges”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin_4.jpg" %}
|
||||
|
||||
Click on “Add user account”.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/phpmyadmin_5.jpg" %}
|
||||
|
||||
Finally, enter a username for your WordPress database user account, pick localhost under “Host name:” and enter a strong password. You will provide these account details to your WordPress installation later.
|
||||
|
||||
### Disabling Root Login
|
||||
|
||||
Let’s secure phpMyAdmin further by disabling the root login.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo vim /etc/phpmyadmin/conf.d/pma_secure.php
|
||||
{% endhighlight %}
|
||||
|
||||
Add this to it, taking care to make any random 32 character string in place of `<random string>`.
|
||||
|
||||
{% highlight php %}
|
||||
<?php
|
||||
$cfg['blowfish_secret'] = '<random string>';
|
||||
$i=0;
|
||||
$i++;
|
||||
$cfg['Servers'][$i]['auth_type'] = 'cookie';
|
||||
$cfg['Servers'][$i]['AllowNoPassword'] = false;
|
||||
$cfg['Servers'][$i]['AllowRoot'] = false;
|
||||
?>
|
||||
{% endhighlight %}
|
||||
|
||||
### HTTP Basic Authentication
|
||||
|
||||
To prevent brute force attacks in the case of someone finding your phpMyAdmin login page, let’s add another layer of authentication before anyone even gets to that page. First you need to come up with another secure password, different from all the other ones you’ve used of course, and run it through the following when it asks:
|
||||
|
||||
{% highlight bash %}
|
||||
openssl passwd -apr1 > pma_passwd
|
||||
{% endhighlight %}
|
||||
|
||||
The OpenSSL utility outputs a really long hash of your password, which we’ll send to the file pma\_passwd for ease of use. Now edit that file and make it look something like this:
|
||||
|
||||
{% highlight bash %}
|
||||
<username>:<hash>
|
||||
{% endhighlight %}
|
||||
|
||||
Replace `<username>`; with one of your choosing. `<hash>` is the hash that was already there when you opened the file. Now let’s move this to the nginx folder.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo mv pma_passwd /etc/nginx
|
||||
{% endhighlight %}
|
||||
|
||||
Remember that config file from earlier in `/etc/nginx/sites-available`? Let’s edit it and add a couple lines so it looks like this:
|
||||
|
||||
{% highlight bash %}
|
||||
server {
|
||||
# other code
|
||||
# replace <str> with your <str> from earlier
|
||||
location /<str> {
|
||||
auth_basic "phpMyAdmin Login";
|
||||
auth_basic_user_file /etc/nginx/pma_passwd;
|
||||
}
|
||||
# other code
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Now restart Nginx again (`sudo nginx -t` and `sudo systemctl restart nginx`) and try to access your phpMyAdmin screen again and you’ll see something like this:
|
||||
|
||||
{% include figure image_path="/assets/img/2019/auth_basic.jpg" %}
|
||||
|
||||
Enter the username and password you used and you should be in. Congrats, your phpMyAdmin is now pretty secure!
|
||||
|
||||
This should deter bots since, as far as my logs from my WordPress login page show, they tend to get the 401 error and then leave. And yes, we will be securing the WordPress login page later too.
|
||||
|
||||
## Installing WordPress
|
||||
|
||||
With everything in place, we finally get to the setup of WordPress itself. We start by downloading and unzipping the source files for WordPress.
|
||||
|
||||
{% highlight bash %}
|
||||
wget -O latest.zip https://wordpress.org/latest.zip
|
||||
unzip latest.zip
|
||||
{% endhighlight %}
|
||||
|
||||
The resulting wordpress folder contains all the WordPress files which we will now move into the html folder of your website. Delete everything in that folder and then move the WordPress files over.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo rm -r /var/www/matthewtran.dev/html
|
||||
sudo mv wordpress/* /var/www/matthewtran.dev/html
|
||||
{% endhighlight %}
|
||||
|
||||
Now head over to your website and you should see the WordPress installation screen. It’s a pretty straightforward process. Just follow through all the instructions, taking care to give it the username and password for the account for the WordPress database from earlier. Also you’re going to create an administrator account for your website so make sure to pick a username that isn’t “admin” and a very strong password as always.
|
||||
|
||||
Congratulations! You’ve done the basic setup of your WordPress server. Now onto fixing some of the little issues that might pop up and some recommendations.
|
||||
|
||||
### Fixing File Permissions
|
||||
|
||||
First, let’s change the group of every file to www-data.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo chown -R $USER:www-data /var/www/matthewtran.dev/html
|
||||
{% endhighlight %}
|
||||
|
||||
To prevent an error about asking for FTP credentials, we need to modify `wp-config.php`. Go ahead and open it in Vim and at the very bottom of the file add the following.
|
||||
|
||||
{% highlight php %}
|
||||
define('FS_METHOD','direct');
|
||||
{% endhighlight %}
|
||||
|
||||
Chances are you won’t be able post or change your themes or anything because of permission errors. Let’s fix those.
|
||||
|
||||
{% highlight bash %}
|
||||
cd /var/www/matthewtran.dev/html
|
||||
sudo find ./ -type f -exec chmod 664 {} +
|
||||
sudo find ./ -type d -exec chmod 775 {} +
|
||||
sudo chmod 660 wp-config.php
|
||||
{% endhighlight %}
|
||||
|
||||
This sets the proper permissions so WordPress is able to modify everything and update itself. Notice that we did `chmod 660` on wp-config.php. That file actually stores the credentials for your WordPress database in plaintext, so it’s imperative that no one except WordPress can access it. We’ll be adding a bit more security to it soon.
|
||||
|
||||
### Securing Important Files
|
||||
|
||||
You may have noticed that anyone can get into your login page simply by typing `<domain>/wp-login.php` or `<domain>/wp-admin` (`wp-admin` actually redirects to `wp-login.php`). There are plugins like Jetpack and WordFence (which I highly recommend installing) that help secure your site against brute force attacks but let’s add another layer of security. The instructions for this are pretty much the same as when we secured phpMyAdmin.
|
||||
|
||||
{% highlight bash %}
|
||||
openssl passwd -apr1 > wp_passwd
|
||||
{% endhighlight %}
|
||||
|
||||
Like before, edit `wp_passwd` with a username.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo mv wp_passwd /etc/nginx
|
||||
{% endhighlight %}
|
||||
|
||||
Now let’s enable authentication in the config file in /etc/nginx/sites-available. We’ll also deny access to wp-config.php outright so no one can access it.
|
||||
|
||||
{% highlight bash %}
|
||||
server {
|
||||
# other code
|
||||
location /wp-config.php {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location /wp-login.php {
|
||||
auth_basic "WordPress Login";
|
||||
auth_basic_user_file /etc/nginx/wp_passwd;
|
||||
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
|
||||
}
|
||||
# other code
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
Now let’s restart Nginx and test it out. If you try `<domain>/wp-config.php` you should get a 403 error. If you try `<domain>/wp-login.php` you should see a place to enter a username and password just like with phpMyAdmin. Perfect!
|
||||
|
||||
### Check Site Health
|
||||
|
||||
Now login into your WordPress installation and go to the Site Health page under Tools. There you can see if there’s anything else that might be wrong. This tutorial pretty much covers all the problems I’ve seen while doing this whole setup on Ubuntu Server, so you should be fine. You might be missing some PHP packages like Imagick, which you should be able to install with a simple `sudo apt install php-<pkg name>`. Google is your friend here. Sometimes a good `sudo reboot` is necessary to fix any latent problems.
|
||||
|
||||
### Recommended Plugins
|
||||
|
||||
One thing to keep in mind with plugins is that you should make sure they’re always updated because of potential security vulnerabilities. Just like how you wouldn’t install a shady or very outdated app on your computer, you shouldn’t install one on WordPress. Even themes can have vulnerabilities. That being said, here’s some plugins I recommend:
|
||||
|
||||
- Jetpack
|
||||
- Wordfence
|
||||
- Akismet Anti-Spam
|
||||
- MonsterInsights
|
||||
- WP Super Cache
|
||||
- Yoast SEO
|
||||
|
||||
### SEO Optimization and Analytics
|
||||
|
||||
Something you might hear about a lot online is SEO optimization. This is so that search engines like Google will rank your website higher, making it more likely for people to find, and ultimately giving your website more reach. There are many things to consider, but I think the Yoast SEO plugin is a pretty good starting point for learning about the various aspects of optimizing your website.
|
||||
|
||||
You might also want to keep track of how your website is doing in terms of visitors. The Jetpack plugin, which also provides a level of security, has some basic analytics of your website. I used Google Analytics before on my other website and was delighted to find out about the MonsterInsights plugin which links to your Google Analytics account and gives pretty good information about your website.
|
||||
|
||||
I also use the Google Search Console to also keep track of how my websites are showing up in searches (current count: 0).
|
||||
|
||||
### Speed Optimization
|
||||
|
||||
Now you may realize that when opening your website that it’s quite slow. The reason for this is that WordPress regenerates your website for every visitor. The plugin WP Super Cache fixes this problem by generating your website and storing it in a cache which then gets served to visitors. This vastly sped up my website, but I do have to press “Delete Cache” every once in awhile when my website isn’t updating properly. One big thing to keep in mind is that images are huge, so make sure to shrink them down and compress them (like by converting to JPEG). I like to keep all my pictures under 50kB if I can. For privacy reasons, make sure you remove all the meta data from pictures before uploading. I use ImageOptim when I’m using my MacBook, but I’m pretty sure a Windows equivalent exists.
|
||||
|
||||
Sometimes the bottleneck for speed on your website comes from the server itself. You can use tools like `htop` and `iotop` to analyze your system under load and see where the slowdowns are. Currently, my storage system is the slowest part, which is to be expected. I’m going to definitely upgrade my server during the next big sale.
|
||||
|
||||
### Security
|
||||
|
||||
There are so many more ways to improve the security of your website, not all of which I’ve even implemented. What I’ve provided so far is a pretty good start at a secure site. There’s more to do but after this point it’s more of the little things, dealing with more permissions and what not. If you want to learn even more about securing your site, I’d recommend checking out some of the links below.
|
||||
|
||||
- <https://www.wpbeginner.com/wordpress-security/>
|
||||
- <https://bjornjohansen.no/block-access-to-php-files-with-nginx>
|
||||
- <https://lamosty.com/2015/04/14/securing-your-wordpress-site-running-on-nginx/>
|
||||
|
||||
One thing to keep in mind is that if you’re using Jetpack or the WordPress app, you need to allow access to xmlrpc.php. It’s technically a vulnerability, but Jetpack and Wordfence protect you from it.
|
||||
|
||||
### Backing Up Your Server
|
||||
|
||||
There’s many backup plugins out there, but it’s good to know how to backup your WordPress installation manually just in case. Basically just copy your entire html file to a safe place. Then login to phpMyAdmin using the account you made for WordPress, click on the database, then export and download the .sql file. Don’t worry about all the errors that show up, just click ignore all.
|
||||
|
||||
Restoring is also relatively simple, just copy over the html folder and then head to phpMyAdmin, login, make a new database, and import the .sql file.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Congratulations! You’ve just set up your very own WordPress server from scratch. It’s quite the journey but it’s rewarding in the end. Now go off and install themes, make posts, or really anything. WordPress is quite an impressive platform and you can do so much on it. Good luck!
|
||||
|
||||
#### Sources (Check these out for more details!)
|
||||
|
||||
- <https://linuxconfig.org/how-to-configure-static-ip-address-on-ubuntu-18-04-bionic-beaver-linux>
|
||||
- <https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04>
|
||||
- <https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04>
|
||||
- <https://www.digitalocean.com/community/questions/wordpress-and-nginx-404-errors-all-but-home-page>
|
||||
- <https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/>
|
||||
- <https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-phpmyadmin-with-nginx-on-an-ubuntu-18-04-server>
|
||||
- <https://www.freshwpthemes.com/tutorial/how-to-create-a-database-for-wordpress/>
|
||||
- <https://www.smashingmagazine.com/2014/05/proper-wordpress-filesystem-permissions-ownerships/>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: CS61C - Computer Architecture
|
||||
date: 2019-12-11
|
||||
categories: school
|
||||
excerpt: A course with a bit of an information overload about the big ideas of computer architecture that ended up being one of my favorite courses.
|
||||
header:
|
||||
teaser: /assets/img/2019/61c_mbothighres.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2019/61c_mbot0.jpg
|
||||
- image_path: /assets/img/2019/61c_mbot1.jpg
|
||||
- image_path: /assets/img/2019/61c_mbot2.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2019/61c_class0.jpg
|
||||
- image_path: /assets/img/2019/61c_class1.jpg
|
||||
- image_path: /assets/img/2019/61c_class2.jpg
|
||||
- image_path: /assets/img/2019/61c_class3.jpg
|
||||
- image_path: /assets/img/2019/61c_class4.jpg
|
||||
- image_path: /assets/img/2019/61c_class5.jpg
|
||||
- image_path: /assets/img/2019/61c_class6.jpg
|
||||
---
|
||||
|
||||
This was easily one of my favorite courses because it operates right at the intersection of EE and CS. I really liked the focus on hardware, performance, and how things are actually implemented in real life. Even more, I learned about a wealth of technical challenges and solutions that I didn’t know much about before. Keeping in line with my love of embedded systems type projects, I was ecstatic at finally getting to learn C, RISC-V assembly, GDB, and Valgrind. In reality, the course focused more on the big ideas side and the coding part was mostly self-taught, but it was awesome nevertheless. Anyways, here’s a couple of projects we did.
|
||||
|
||||
## Mandelbrot
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
We generated pretty mathematical images using C. This project served as a nice dive into C and memory management. Overall, it was a very straightforward project and I really liked how I felt that I knew almost exactly what the computer was doing behind the scenes.
|
||||
|
||||
After we learned about OpenMP and SIMD, we had a lab on vastly improving the performance of Mandelbrot because it’s an “embarrassingly parallel” task. To simplify things, we only had to determine if a point was in the set or not. I ended up getting a 31x speedup over the serial version, which really opened my eyes to how much multithreading and other parallel techniques can improve performance.
|
||||
|
||||
## CS61Classify
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
Our next project was to implement an Artificial Neural Net (ANN) to identify numbers from the MNIST dataset. This definitely sounds more impressive than it actually was. All we did was write a couple functions to do dot products and matrix multiplication. The details of training and running the actual neural net were all handled for us. The real challenge was doing it all in RISC-V assembly. It was quite fun and gave me a better understanding of how a higher level language like C can get directly translated into machine code.
|
||||
|
||||
## CPU
|
||||
|
||||
{% include figure image_path="/assets/img/2019/61c_cpu.jpg" %}
|
||||
|
||||
Last but not least, we implemented the logic for a RISC-V processor in Logisim. We made everything from the ALU to the immediate generator to the control logic. When it came time to put it all together, I started by organizing all the parts into the same format as the picture of the data path from lecture. Keeping everything neat, organized, and aesthetic is kind of my thing. Then I added everything else.
|
||||
|
||||
One interesting part of the project was the extra credit for the ALU. We had to implement the mulh instruction which gets the upper 32 bits from multiplying two 32 bit numbers. There’s no built-in Logisim block for this, so I did some algebra to figure out how to do it. A bit messy, but it worked perfectly.
|
||||
|
||||
The most tedious part of the project was the control logic. We had to provide the correct control signals for each part of the data path to support each instruction. I started by making a spreadsheet for each instruction and the control signals necessary. Then I spent a good 6 hours looking at each and every control signal and what parts of the inputs determine them. The number of times I said “uniquely determine” is countless. As far as I know, it was a slightly unorthodox approach, but very compact and clean. Well worth the headache.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: CS170 Final Project
|
||||
date: 2019-12-12
|
||||
categories: school
|
||||
excerpt: The thrilling final project for CS170 tasked us with finding the best algorithm for approximating the optimal solution for an NP-hard problem.
|
||||
header:
|
||||
teaser: /assets/img/2019/170_graph.png
|
||||
---
|
||||
|
||||
As the finale to an amazing algorithms course, we had to tackle a problem called Drive the TA’s Home. It is setup as starting at a certain node in a graph with a certain number of TAs which all live in unique nodes on the graph. The goal is to find a cycle of nodes to travel through and a list of drop offs that minimizes energy cost. Dropping a TA off at a node makes them travel the shortest path to their home, with an energy cost of 1 per unit distance traveled. Driving along an edge has an energy cost of 2/3 per unit distance traveled. We weren’t looking for an exact algorithm but instead the best approximation we could.
|
||||
|
||||
Since the project changes every semester, I made our repo public, which you can find [here](https://github.com/dragonlock2/RaoTA).
|
||||
|
||||
## Algorithms
|
||||
|
||||
My teammates and I came up with quite a few algorithms in our search for the best outputs to the 949 inputs we were given. We also iterated many times and improved performance a lot along the way. Here’s descriptions of our algorithms, in no particular order. In the interest of time, I’ll keep them short.
|
||||
|
||||
### Brute Force (brutepython, brutecppLVIII, brutecppCC)
|
||||
|
||||
In order to provide a baseline we could compare our other algorithms against, I started by creating a brute force algorithm. To ensure we were finding optimal solutions, I used an object oriented approach with a Car object that has both a location and a set of TAs. We start with a Car at the starting location that has all the TAs. Then I used Dijkstra’s algorithm to search for the path of smallest energy cost to a Car at the starting location with no TAs. Conceptually, the “neighbors” of each Car involve all the possible nodes it can travel to and all the possible combinations of TAs it can drop off. The “cost” of the edge to that “neighbor” is just the cost of the drop offs and the cost to travel to that next node.
|
||||
|
||||
Improving performance of the algorithm was quite interesting. After optimizing the initial Python implementation as much as possible, I decided to convert it to C++. Based off some rough calculations, I reasoned that it would be barely possible to brute force the smallest size inputs we would be given, 50 nodes and 25 TAs. It took a lot of effort, a complete restart, and serious optimizations including bit manipulation and memory allocation, but I learned a lot and achieved a 100x speedup over the Python implementation. After using cProfile and Callgrind, I found that both my Python and C++ implementations probably couldn’t be improved much further, so I moved onto the rest of the project.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/170_callgrind.jpg" %}
|
||||
|
||||
### Sorted (sortedpython)
|
||||
|
||||
Even with the optimized brute force algorithm, it was still not fast enough to do anything above 16 TAs. As a first attempt at improving speed, to generate neighbors I tried sorting TAs by distance from their home and only dropping off farther TAs if I dropped off closer ones too. While an improvement over the naive solution of dropping everyone off immediately, this did not end up working too well.
|
||||
|
||||
### Minor Improvement (minorimprove)
|
||||
|
||||
Thinking about the optimal solution, I reasoned that no edge is double traveled by TAs on their ways home. Also, there’s probably room to improve the cycle traveled to get to each drop off point using a TSP approximation. Based on these two observations, I wrote an algorithm that takes an existing output and improves it. However, it does rely on having a very good one already. Also, since I used Google OR-Tools there were cases where it did perform worse.
|
||||
|
||||
### Optimized TSP (optimizedtsp, optitsp)
|
||||
|
||||
My teammate Joe came up with this algorithm. It’s based off the observation that dropping TAs off at their homes and using TSP to find a cycle actually works pretty well. First, he used LocalSolver’s TSP solver to find an order to drop off TAs in. To optimize further, for each TA, he’d consider all other nodes as drop off points and use the one with minimum cost. Repeating this multiple times ended getting very close to optimal for a lot of cases.
|
||||
|
||||
As we were working on our Semitree algorithm, I rewrote this algorithm as a subroutine. Since I didn’t want to wait a day to get a LocalSolver license, I used Google OR-Tools. Later on, I moved over to Gurobi which improved performance even more. I also added on the drop off cycle optimization from the minor improvement algorithm to further reduce cost.
|
||||
|
||||
### ILP Reduction (gurobilp)
|
||||
|
||||
Talking to a bunch of HKN people, I realized that all of them used an ILP reduction for their algorithms. It was then that I learned about Gurobi, an ILP solver that’s among the best in the business. Hearing everyone keep talking about finding “provably optimal” solutions got me interested. Three nights before the deadline, I took a look at the TSP ILP reduction. A couple hours later, I had an ILP formulation.
|
||||
|
||||
Basically, like the TSP ILP reduction I’d have a binary variable `e[i,j]` for each pair of nodes, but directed instead to simplify things. There’s also binary variables for each TA for each node, called `v[i,k]`. I also had indicator variables for if a node is a drop off point. For the constraints, I first had to make sure each TA is dropped off and only once. Then for each drop off point, make sure we pick one edge leaving and one edge entering it. My first reduction had a couple more variables and constraints, but with some help from my teammate Kevin we got rid of them and improved performance significantly.
|
||||
|
||||
### Semitree (semitreeTSP, semitreeILP)
|
||||
|
||||
My teammate Kevin came up with easily the best algorithm we had. He worked on his own implementation, but I also did my own using a different perspective of the algorithm, which I’ll explain. Basically, we’re looking for nodes whose removal splits the graph into subgraphs, which are called articulation points. We’re also looking for biconnected components, which result from removing articulation points. We generate a tree-like structure based on these points and components. From the starting location, we look at all the articulation points in each biconnected component it’s in. For each point, we look at its child subgraphs. If there’s one TA total in them, then we replace the point with a fake TA. If there’s more than one, then we replace the point with a fake TA, force a visit there, and recurse on it.
|
||||
|
||||
To find a solution for the resulting biconnected components, we’d employ one of our other algorithms, mainly Optimized TSP and ILP Reduction, which I modified to support forced visits. After solving subgraphs, we’d stitch together the different parts to generate the final output. This algorithm worked so well because it employed provably optimal techniques in a divide and conquer fashion to break down the problem.
|
||||
|
||||
## Generating Outputs
|
||||
|
||||
In order to streamline the process of generating outputs, I setup a framework of multiple scripts to automate tedious tasks. The main one was bigsolve.py which ran a specified algorithm on certain inputs. The real benefit was that it only wrote to the output folder if the algorithm’s solution was better than the existing one. This allowed us to quickly try different modifications and algorithms without worry.
|
||||
|
||||
Since we found out about Gurobi less than three days to the deadline, we really had to make the most of our time. In an unfortunate turn of events, I sent my gaming computer and some Ryzen processors I bought for Black Friday home to work on during winter break. Literally the only computers we had were my 2018 Macbook Pro, Joe’s 2016 Macbook Pro, and one of the Hive machines. Luckily, due to heavy optimizations in our algorithms and good organization we were able to generate “optimal” outputs for all but about 180 inputs.
|
||||
|
||||
As for the breakdown of how each algorithm did, about ~80 of them were solved optimally using my brute force C++ algorithm. A good ~700 of them were solved “optimally” using semitreeILP, which used the ILP reduction as a subsolver. The rest of them had very good approximations generated using semitreeTSP, which used Optimized TSP as a subsolver. In fact, semitreeTSP could generate outputs for all 949 inputs in under an hour and was within ~2% of optimal if not optimal in most cases.
|
||||
|
||||
## Conclusion
|
||||
|
||||
At the end of the day, I did not expect this project to have as much an impact on me as it did. True to my personality, I tackled it first and foremost as a software engineering problem. I setup a framework that made development and testing of the algorithms we’d develop straightforward. Comparing against the brute forced optimal solution and naive solution helped guide our efforts and led to some discoveries that inspired our best algorithms. Later on, I dived into the theory side to help come up with even better algorithms. Although our team did end up getting 5th out of about 366 teams, I really appreciated how much I learned in the process. More than anything, I really could not have done it without my teammates.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: MMSim
|
||||
date: 2019-12-12
|
||||
categories: projects school
|
||||
excerpt: Just for fun, I wrote a simulator for the flood fill algorithm for the Micromouse DeCal I helped teach this semester.
|
||||
header:
|
||||
teaser: /assets/img/2019/mmsim_0.png
|
||||
---
|
||||
|
||||
Out of the blue, I decided to write a simulator in Python for the Micromouse DeCal I helped teach this semester. Past semesters of Micromouse never got around to doing the actual maze solving and I felt that part of the reason was not having a good explanation or reference implementation of the algorithm. Flood fill is the de facto standard when it comes to this thing so I decided to learn it and implement it. Due to all the power outages and midterms, we ended up not getting to maze solving yet again but we’ll definitely do it next semester.
|
||||
|
||||
Here’s a [link](https://github.com/dragonlock2/MMSim) to my repo.
|
||||
|
||||
## Maze Representation
|
||||
|
||||
First, I came up with a simple and efficient way of storing mazes. Since mazes are constructed as a bunch of square cells with walls placed between them, I looked into a Cartesian coordinate system. For each cell, we can have north, south, east, and/or west walls. We represent having a wall in that direction as having the corresponding bit be a 1. This will make it easier to convert to Arduino later. I then setup a bunch of code to help read maze files and display them.
|
||||
|
||||
## Flood Fill
|
||||
|
||||
The flood fill algorithm is conceptually pretty simple. It can be modeled as water flowing along the fastest path from highest to lowest elevation. As the robot moves through the maze, it reconstructs it in memory, which necessitates updating the “heights” of each cell. There’s lots of room for improvement in the future, but my algorithm currently only considers the Manhattan distance between cells and constantly recalculates all heights using BFS.
|
||||
|
||||
## Robot Representation
|
||||
|
||||
As for the robot, I used an object oriented approach, naming it Karel after the way I was taught Java in high school. I modeled Karel with the knowledge that the algorithm would eventually be moved over to C++ on the Arduino. As such, I made sure to write code as non-Pythonic as possible and to reduce memory usage. To start, Karel has a couple of sensors which can tell us if there’s certain walls around us. It can also turn left, turn right, and move forward. With each movement, we gain new information about the maze and so update our reconstruction accordingly. Just having a couple of sensors and move methods is all that flood fill needs to find the shortest path to the target.
|
||||
|
||||
{% include figure image_path="/assets/img/2019/mmsim_1.png" %}
|
||||
Reference in New Issue
Block a user