Sunday, March 2, 2014

Audible Altimeter part 1

I was looking for a new electronics project to work on, and found some inspiration in the skydiving world. While you're in freefall, it's absolutely critical to know what altitude you're at, so you can know when to safely pull your parachute. Skydivers wear something along the lines of this so they can keep track of altitude, but another useful indicator is what's called an "audible altimeter", an electronic device that's placed in your helmet and sends out a series of warning beeps at pre-programmed altitudes. I was looking into purchasing an audible altimeter for myself, and found that they're all super expensive (over $200), which seemed pretty ridiculous to me considering how simple they are. I decided it would be a fun project to try and make my own since I thought I could produce one for cheaper, and learn a lot in the process.

It's definitely an interesting project because everything has to be made as small and low-power as possible, in order to have it be completely portable and so that you don't have to constantly worry about the battery dying. I decided to go with a rechargeable lithium polymer battery (specifically this one) since they have great energy density and charge rates. I found a fantastic chip which is built to charge a single cell LiPo at up to half an amp from a 5V source, which is great for USB charging. I'd also gotten some experience soldering QFN (leadless) surface mount parts over the summer, and I thought it'd be neat to design a board with these to make it as small as possible.

I wanted to branch out in microcontroller selection and pick a new architecture that I could learn about. I ended up going with an STM32F050 because it's quite powerful for how much it costs, and it's a good match for the features I need.

Other than that, I picked:
  • fast barometer (many of them could only be read as fast as once a second, but this one gives up to 200 readings per second) so that I can get an accurate reading of pressure to calculate altitude from. 
  • a loud 4KHz buzzer so that it would be easy to hear
  • an FTDI USB to UART bridge so that I could program it over USB
  • a 3.3V buck converter, because the LiPo battery outputs anywhere from around 3.6 to 4.2 volts, and everything runs on 3.3V. The buck converter isn't completely necessary because it could be replaced with a linear regulator, but I figured it was worth it to pay a few cents more for something like 20% longer battery life due to the extra efficiency.
  • a bunch of 0603 (metric 1608) passive resistors, capacitors, and LEDs, as well as a few miscellaneous parts like buttons, an inductor, etc.
I started out with a quick block diagram of how I wanted to hook everything up:


I forgot to label Vdd from the STM and the BMP180, but they're going to the 3.3V buck output, not the battery voltage.

I basically sat down for a day or two and CADed all of the part packages using Eagle, and put together a schematic that pretty closely follows the block diagram above, plus a bunch of passives, buttons, and a couple other things.


Unfortunately I was in one of those ultra-focused "make this project without thinking about anything else" states while putting this together, so I forgot to take screenshots showing the process.

One interesting challenge was that I wanted the whole board to be completely powered off while not in use, turn on when a button is pressed, and then be able to turn off when the same button is pressed again. Since I'm only using on/off tactile buttons, I had to get a little creative in order to build this behavior. Basically the buck converter's enable pin (EN) is hooked up to a microcontroller output, so the microcontroller can drive it low to turn off power to everything (including the micro itself). The power button is also hooked up to EN, so that when the board is off and you press the power button, the buck converter turns on, turning on power to the whole board. The micro then has to immediately hold EN high in order for the buck to stay on when you release the button. So far so good, right? The one problem is that the micro has to have a way of knowing when you hit the power button a second time, so that it knows when to turn off the buck and power down. I ended up coming up with a resistor divider pulls a pin on the micro low whenever the micro is driving EN, and pulls the pin high when the button is driving EN, allowing the micro to read the state of that pin to decide if it should power off.

With the schematic out of the way, it was time to lay out/route the board! I wanted all the parts to be on one side so that I could put a battery flush on the other. Here's what I was looking at after placing components:


You can't tell from the picture, but the battery connector (bottom right) is actually on the underside of the board to make it easy to plug the battery into. The yellow lines are "airwires", or connections that haven't yet been physically made. The whole board comes in pretty small at just under 2" by 1", which I'm pretty happy with.

Routing time! This is one of my favorite parts of making boards, it always creates some interesting challenges, especially with smaller boards. Once again, I was in in-the-zone-mode, so I forgot to take pictures of the process.


Looking back I'm a little unhappy with what I consider slightly sloppy routing, all those vias (connections from one side to the other) in the center look pretty messy. Oh well, for revision 1, as long as it works right?

I decided to go with Seeed Studio to get the board fabricated, for smaller boards like this they have an awesome deal of 10 boards for $10, not counting shipping. After placing the order, it was time for the few weeks of eager anticipation that I've come to know over the last year or two since I started making boards.

Of course, it's always worth it when you get a package in the mail and suddenly it feels like Christmas. Here's the top and bottom, with a quarter for scale:


Sorry for the picture quality, maybe one of these days I'll stop spending all my money on skydiving/engineering projects and actually buy a camera instead of using my phone.

Of course as soon as I got these I put everything else aside and rushed to populate one of these with parts, so here's a picture taken a few hours later with everything soldered on:


From there I moved on to trying to turn on the board, and immediately starting having a few problems with the buck converter that it took me a while to get to the bottom of. After decreasing the bypass capacitance on the VCC line I was able to get it to boot up, which allowed me to start programming the microcontroller. Time to learn how the STM32F050 works! Fortunately there's a lot of example code and guides around which make it pretty easy to get things going. I won't go into to much detail about how the code works, I don't think it's useful to paste a bunch of lines of initialization/etc here, so instead I'll describe what I generally did. I started out writing a basic program which holds the buck's EN pin high, turns on the yellow LED, and turns on the blue LED whenever the "ZERO" button is pressed. After getting that all working, I noticed some weird behavior where the board would turn off seemingly randomly. I eventually realized that putting my finger on the top of the board was what was causing it to turn off, most specifically whenever I touched the buck converter. Hmm. It turns out that I had used a large resistor in my voltage divider (the one described above) which caused the EN pin on the buck to be sort of floating, and the transient produced by touching it with your finger was enough to bounce it low, causing the whole board to turn off. Once I knew about this I actually elected not to patch in a bypass capacitor on EN (which would fix the issue), because while debugging I actually prefer the option to have a quick "hard off" available (where the off button is basically touching the EN on the buck).

From there I moved on to figuring out how PWM works on these chips and wrote a function to drive the piezo at different frequencies (e.g. a low frequency/relatively quiet 'boop' when it boots up). I continued to fiddle with it for a week or two, then more or less set the board aside for a while to focus on schoolwork. This semester I have a bit more free time, so I picked the board back up a few weeks ago and continued to work on the code for it. I²C, the communication protocol that the barometer uses, was particularly difficult to get working. I learned that I should have included an extra header for debugging I²C because I had to measure everything by jamming scope probes into the tiny vias on the I²C signal lines. After a few days of working on it, though, I finally got the two chips successfully talking and was able to get a pressure reading out of the barometer.

I took a snapshot of the communication between the microcontroller and the pressure sensor with an oscilloscope:


The other board you can see is ST's awesome Discovery board, which runs for $10 and has an STM32F1 that you can program, as well as a built-in ST programmer which I'm using here to program my board. On the scope, you can see that the first eight data bits (blue) write out 11101110 where they coincide with the clock pulses (yellow). Converted to hex, this is 0xEE, which is the 'write to' address of the barometer, meaning that I'm telling it I'm about to write data to it. The next clock pulse after this is the barometer pulling the data line low, which is an I²C 'ACK', or acknowledgement that it received the previous byte. After that, you can read 11110110, or 0xF6, which is the micro telling the sensor which address it wants to read from (in this case, the address where the last pressure reading is stored). Not currently shown on the scope, the altimeter then sends out its 'uncompensated' pressure reading, which the micro uses (along with a variety of factory calibration parameters previously read from the barometer's memory over I²C) along with a temperature to calculate the 'true' pressure, and then calculates altitude from the pressure.

I had been debugging everything from my laptop, and using debug mode to keep track of variables, which I decided wasn't a great solution for actually tracking how the measured altitude changed as I moved around. I ordered a cheap LCD from ebay and threw it on a breadboard with an Arduino to try and get a portable display which would read out the altitude difference from the zero point in real time, allowing me to run up and down tall buildings and see how well it tracks changes. Because of the way the I²C protocol works, you're able to add new devices to your communication lines whenever you want, which allowed me to simply add the Arduino onto the existing I²C line in order to talk to it. Unfortunately this required somewhat sketchily soldering thin wires onto very small pads on the pull-up resistors on my board, but once I got them there I coated them with hot glue to keep them in place.


Unfortunately, while for some reason the Arduino reads its address correctly (sends an ACK) and does appear to read some of the data sent to it, I've got a bug somewhere because somehow it's not actually reading the data I'm sending. It'll probably take another few days of poking at the Arduino to get it to read data in properly, but my board itself seems to be working as I want it to, for the most part. Pretty close to having it actually be usable for skydiving! I'll probably 3D print a case for it as well.

That's the current state of my altimeter board, I've also got a new project that I'm really excited about (and just ordered boards for!) which I'll write up in the next post.

Saturday, January 18, 2014

I'm back - let's write some assembly!

Hey! It's been a while. I'll go ahead and explain where I've been, and then talk about some stuff I've been working on recently. I stopped posting before the start of last summer, where I got to intern at a really cool company called Freefly, who primarily make giant camera-hauling multirotors for professional cinema, as well as more recently a sweet 3-axis handheld camera stabilizer. They're based in my hometown, Seattle, and staying there was pretty awesome because I got to rock climb every weekday evening, and then head to the dropzone and skydive every weekend! Unfortunately this left me without a lot of time for personal projects, but I did manage to pick up my skydiving license towards the start of the summer, and I'm up to 54 jumps now. Here's a pretty cool picture my friend Andrew from the MIT Skydiving Club took of me this fall:


I also have a video from early in the summer, just after I got my license. I'm wearing all black with a black helmet and a navy blue parachute with red highlights:


Anyway, enough about that. Let's move on to the really interesting stuff - x86 assembly! Last semester I took a pretty cool class called Intro to Robotics, in which we were provided with 3D printed, jointed legs, and had to build a driven exoskeleton to allow them to walk on a treadmill. I was primarily in charge of my team's programming and electronics (it was a MechE class so I had the most experience in these), and I was able to write some functional code and get it to walk:


The robot uses a gyro inside the hip to determine the hip angle, and has a control loop which speeds up the walk cycle as the hip tilts forwards, allowing it to match the speed of the treadmill. I also got to witness a bit of control system magic when the robot walked up on the front piece of the treadmill (stepping off of the belt entirely), paused for a second, and then started walking backwards back down onto the treadmill. Since walking too far forward had caused the hip to tilt backward, it actually slowed down the walking cycle until it reversed it entirely. Unfortunately this wasn't captured on video, but it was a pretty magical moment!

Despite this, I wasn't too proud of my code because to me it felt pretty hacked together. I decided I really wanted to make an effort to get better at programming - of course step one was to immediately add a linux (Ubuntu) install to my laptop, and after working on a few Python projects I decided I wanted to try and learn some assembly language. Despite having spent a fair amount of time with various microcontrollers, everything I've written for them has been in C, so I didn't have any experience writing assembly until now. I thought that it would be fun to write something that runs on my laptop, so I installed nasm, a common assembler, and started reading online tutorials. I'll try to keep this post limited to explaining what I've done, rather than making it an assembly tutorial on its own, but I'll link you a few of the tutorials/pages I found useful if you're interested in really learning this stuff on your own:

http://docs.cs.up.ac.za/programming/asm/derick_tut/ - very useful tutorial, explains procedures, the stack, jumping, and just generally how to get started.
http://leto.net/writing/nasm.php - a very similar tutorial, slightly less in-depth
https://www.cs.uaf.edu/2005/fall/cs301/support/x86/nasm.html - a cheat sheet with some common commands
http://home.myfairpoint.net/fbkotler/nasmdocc.html - the entire nasm instruction set reference
http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64 - a table of system calls (more on what that is later) for 64-bit linux

Unfortunately all of those tutorials are for 32-bit linux, and since I wanted to write for my 64-bit machine I had to use different registers than the ones in their examples, as well as having entirely different system calls.

First let's go over what I wrote, and then I'll explain it a bit. I started out writing a program that takes in a command line argument (when you run a program from the command line you can give it one or more 'arguments', which are just strings that get pushed to the stack) and then prints it back out. This really isn't useful to me (more useful versions of this already exist, like echo), but it's a good place to start learning. Here's the behavior:

The battery monitor at the bottom [charge state, charge%, remaining time] is one of the Python projects I mentioned earlier
You'll see me calling the program to run it: ./args_old, and then giving it a single string: "this is a sample string!" as a command line argument. The backslashes are just escapes for the spaces and exclamation mark, so they become part of the string instead of splitting the text into separate arguments. As you can see, running it will just print the string I gave back through standard output.

Now, let's take a look under the hood:

section .text
global _start

_start:
pop rax ; get number of args
pop rbx ; get program name
cmp rax,2
jne _bad; if there isn't just 1 arg, go to _bad
pop rcx ; first argument

call _length_entry

_output:
mov rax,1
mov rdi,1
mov rsi,rcx
mov rdx,[strLen] ; length of str into rdx

syscall ; kernel halp

_newline:
mov rax,1
mov rdi,1
mov rsi,ten
mov rdx,1
syscall

_exit:
mov rax,60 ; exit
mov rdi,0 ; don't error
syscall ; laterz

_bad:
mov rax,1
mov rdi,1
mov rsi,feelbad
mov rdx,feelLen
syscall
jmp _exit

_length_entry:
mov qword [strLen],0
jmp _length_test

_length:
add qword [strLen],1
add rcx,1

_length_test:
cmp byte [rcx],0
jne _length
sub rcx,[strLen]
ret ; if null byte, ret

section .data
strLen dq 0 ; reserve some space for length
ten db 10 ; -_-
feelbad db 'Your args are bad and you should feel bad.',10
feelLen equ $-feelbad

Woo, assembly! 60+ lines just to print something back at you. In order to make a system call to write, you have to give it both a string and the length of the string, so it knows where to stop. Because of this, I had to write a routine that counts the length of the argument, conveniently named _length. Basically it uses the strLen as a counter, counting characters in the string until it hits a null byte (0), meaning there are no more characters and it's hit the end of the string. The register rcx holds a pointer to the argument string from the start. It gets shifted forward in each loop of _length to always point to the next character until finding the null byte, then it gets subtracted by strLen so that it points back to the beginning of the string. Also, if you don't have exactly one argument, it'll jump to the label _bad which prints an error message. Finally, the _output routine sets registers to make a system write call, puts the string pointer into the rsi register which is where the system looks for the string to write, and similarly puts strLen into rdx. Then syscall actually tells the kernel to run the instruction.

Once I got that working, I decided to make it a bit more interesting. How about making it take in two arguments, both a number %n and a string, and then printing the string %n times?



Because arguments are always pushed to your program as strings, this means that something like "3523" is still a string, so I had to write a routine to convert a string into an integer. Here's the full code for the new version (also a pastebin):

section .text
global _start

_start:
pop rax ; get number of args
pop rbx ; get program name
cmp rax,3 ; make sure there are 2 arguments (+1 for program name)
jne _bad ; if not, jump to error message
pop rdx ; first argument
pop rcx ; second argument

call _length_entry
mov [stra],rcx
call _atoi_entry
call _text_entry
call _exit

_output:
mov rax,1
mov rdi,1
mov rsi,[stra] ; move pointer of the string start into rsi
mov rdx,[strLen] ; length of str into rdx
syscall ; kernel halp

_newline:
mov rax,1
mov rdi,1
mov rsi,ten
mov rdx,1
syscall
ret

_exit:
mov rax,60 ; exit
xor rdi,rdi ; don't error
syscall ; peace out

_bad:
mov qword [stra],feelbad ; write feelbad to output string
mov qword [strLen],feelLen ; write feelLen to output string length
call _output
jmp _exit

_length_entry:
mov qword [strLen],0 ; zero string length
jmp _length_test

_length:
inc qword [strLen] ; increment string length
inc rcx ; string pointer to next character

_length_test:
cmp byte [rcx],0 ; check if null character
jne _length
sub rcx,[strLen] ; set rcx back to beginning of string
ret ; if null byte, ret

_atoi_entry:
xor r9,r9 ; zero accumulator
mov [asciinum],rdx ; write string pointer to [asciinum]

_atoi:
xor rax,rax ; set rax to 0
mov r10,[asciinum] ; pointer to asciinum in r10
mov al,[r10] ; first byte of string into first byte of al
cmp al,0 ; make sure we haven't hit end of string
je _atoi_exit

lea r11,[r9*8]
lea r9,[r11+r9*2] ; multiply r9 by 10

sub al,48 ; ascii -> digits
cmp al,9 ; make sure they're numbers 0-9
ja _bad

add r9,rax ; incriment accumulator
inc qword [asciinum] ; shift pointer to string up by 1 (next character)

jmp _atoi

_atoi_exit:
mov [asciiInt],r9 ; write accumulator to asciiInt
ret

_text_entry:
xor r12,r12 ; zero a counter
jmp _text_mult_cmp

_text_mult: ; text_mult is effectively a while loop that prints our
; string r9 times, since r9 is our accumulator from before
call _output
inc r12

_text_mult_cmp:
cmp r12,r9
je _exit
jmp _text_mult


section .data
stra dq 0 ; reserve space for string pointer
strLen dq 0 ; reserve some space for length
asciinum dq 0
asciiInt dq 0
ten db 10 ; sigh. set the value of ten to be 10
feelbad db 'Your args are bad and you should feel bad. Proper syntax: args <int> <str>'
feelLen equ $-feelbad ; set length of feelbad to feelLen


You can see the label _atoi which converts a string to an integer. It's similar to _length, except each time it moves to a new character, it multiplies the whole thing by 10. This makes sense if you think about how a base-10 system works, if you type in a single digit - say, 4 - then it reads the first character, adds it to the accumulator register r9, finds that the next character is a null byte, and writes 4 to asciiInt. If you give it two digits - how about 24 - then it reads the first character, adds it to the accumulator (now the accumulator = 2), finds the next character isn't a null byte, multiplies the accumulator by 10 so the accumulator = 20, reads in the next character (4), and adds it to the accumulator to get a value of 24. The algorithm works the same way as you continue increasing the length of the number you give it. My program also checks each character with cmp al,9 then ja _bad which makes sure it's 0-9 instead of some other ascii character, and if not then it goes to the _bad routine to print an error message.

Here's a quick demo of the error message:


It also still prints the error message if the number of arguments given isn't exactly 2.

That's pretty much it!


Once you write assembly code like my example above, you need to assemble it into machine code, which I do with nasm -f elf64 args.asm. This produces an object file (args.o) which is just raw machine language. There's also a program called objdump which can print out the object file and the associated instructions if you do something like objdump -d args.o, the output of which can be found here on pastebin (check it out, it's cool!). After making the object file, I then use the linker ld to convert it into an executable, for example ld -s -o args args.o which produces an executable file called args. To debug, there's a great program called gdb which allows you to step through your code one instruction at a time, and check all the register values at each step.

Unfortunately the speed of my program is pretty much limited by the overhead of the system write calls, but I still got it to write a 10 character line 10,000,000 times (into /dev/null which is a place to dump unwanted text) in about 6 seconds.

If you're running 64-bit Linux and want to try this out for yourself, you can download the executable from https://www.dropbox.com/s/7t6shbarnr1zvcw/args.

I may not have made any explicitly useful programs, but I definitely learned a lot about how my computer works by learning some assembly. In the future if I need to write a function to do some really fast math, I could definitely see myself implementing it in assembly then calling it every time I need it. I definitely recommend doing this kind of thing on your own, it's fun!

In addition to programming, I've also been doing a fair amount of electronics work recently. Next post will be a write-up of a new board I've put together!

Sunday, March 10, 2013

The Long Overdue Go-kart Post: The Birth of SmartKart

So, I'm probably going to have to give a little bit of background on this one. 2.007 is one of the primary design/build courses for MIT mechanical engineering undergrads, and also served to inspire tons of other robotic competitions across the globe (eg FIRST). Incidentally, the class is also one of the reasons that high-school me was so enraptured by the idea of attending MIT. And now, like so many funny twists that life often brings, I can safely say that I will never take 2.007. I probably wouldn't have believed you if you'd told me that, even just a year ago. But when I found myself in a position to actually sign up for the class, I realized that there was no longer much reason for me to do so. Through clever (not really, this was actually totally accidental) selection of my requirements for my 2-A degree, I'd inadvertently rendered 2.007 no longer necessary for me to graduate. Additionally, by the time I got to the point where I'd be taking 2.007, I felt I'd gathered enough design experience to render the class largely redundant for me. Sorry to disappoint you, high-school me, but it turns out the alternative is even more fun.

And what is this magical alternative, you ask? Well, to be honest, it's not really even a real thing. More just a chain of serendipity leading to me racing around in a go-kart and teaching other MechE's how to build their own. It all pretty much started when I decided that I really wanted to build a hub motor-powered scooter, and solicited advice from MIT's resident silly-hub-motor-powered-thingsCharles. Basically, this initiated a new major interest in electric vehicles for me, and after going to the 2012 Maker Faire last October and witnessing all the crazy go-kart racing, I knew what I had to build next. Fortunately, Charles had run an electric vehicles special section of 2.007 last year, and had plans to bring it back as a more specific go-kart section this spring. I didn't want to wait and take the section for a number of reasons; such as not wanting to wait for a whole semester before starting, as well as not wanting one of my projects to be given all the additional constraints that come along with being part of an academic class. This was about when a rather clever idea came to mind: Charles was a grad student at the time, and an interesting property of grad students is that they're able to acquire UROPs. So, naturally, I decided that I wanted to UROP for Charles and build a go-kart. Conveniently, this actually lined up exactly with what he was looking for at the time: someone to run a sample, 'pilot' of the 2.007 go-kart section (affectionately dubbed '2.00gokart') in order to iron out kinks in the intended curriculum, and give a better idea of what was expected for the course.

Administrative details handled, I pretty much dove right in and started designing. Initially the proposed budget for the course was $300 for the kart (not including batteries, three sticks of 80/20 aluminum extrusion, and 24" x 24" plates of both 1/4" and 1/8" aluminum), so I went with just about the cheapest option for motors I could find: two CIMs. If you did FIRST, you almost certainly remember these. Since the rated 337W max output of the motors sounded a bit low to me, I opted to run them on 24V batteries instead of their rated 12. For reasons that I'm going to try not to go into too much detail about, this effectively bumps their maximum power by a factor of 4, up to 1348W. I whipped up a couple of MATLAB plots to show both conditions:


 As you can see, the motor's power output is the product of its speed*torque, which has a maximum at the halfway point of each. However, owing to the stall current of each motor (133A each at 12V), I'll never actually be able to hit anywhere near their max power output. At 24V, each motor would draw 266A at stall, or 133A at max power output, for a total of 266A for both motors operating at max power. What's the problem with that? Well, that's 1348W*2 = 2696W of power output at the motors, but unfortunately both the batteries I was given and the controller I selected are limited to 100A. 100A*24V = 2400W maximum going into the controller, so I'll never be able to hit 2696W output (not to mention the ~15-20% loss due to resistance in the motor).

Why am I okay with this? Well, the controller is able to limit its output to 100A so nothing in it will explode, meaning I'll be capped at 2400W when I'm getting going. However, as soon as my motors are spinning faster than 556 rad/sec (5310 rpm), I'll be to the right of the max power point, meaning my motors will be outputting less power but operating in a more efficient region (less torque generally means more efficiency, because torque is related to current, power loss is I2*R, so minimizing torque/current means less power loss). How fast do I need to be going to make this happen? Well, I chose a relatively high (fast) gear ratio of 13:70 coming off of my motor, and I'm using 8" diameter wheels. At 556 rad/sec, I'll be going 556 rad/sec * 13/70 * 4 in = 23.5 mph, so any faster than that and my efficiency starts improving.

Okay, enough math, on to CAD:


I started with a frame mock-up, which consisted of spending somewhat inordinate amounts of time slicing up 80/20 into chunks and then mashing it together into a shape I more or less liked (funnily enough, that's a decent summary of the physical build process as well). I knew I wanted to keep the frame fairly short and compact, because I wanted it to be (relatively) portable and lightweight.

Next I threw in motor mounts and brackets:


I used 1/4" plate for all the primary structural frame brackets, because I was worried about 1/8" plates bending. At this point I started building whenever I had parts in and then CADding in parallel. This was probably a poor methodology for design, but I had a fairly complete structure in my head of how the cart would turn out, and certain elements like brake calipers and the seat were impossible to CAD until they were physically in front of me to measure for mounts.


Here's how the final CAD turned out. This assembly doesn't contain a few things, so I'll just go ahead and describe them/mention why they aren't present:
  • Battery/electronics mounts, because I quickly whipped them up in 2D and didn't update the CAD with them (mostly laziness here)
  • Steering wheel, same as above
  • Seat mounts, because the seat was rather organically shaped which forced me to produce a mount by hand, as such it was more something that I just threw together rather than something meticulously planned.
So, on to building! I started out by cutting some of the 80/20 frame elements on the MITER saw.



I then got my CIM motors in, which ship with a keyed shaft:



In order to interface this with the small, smooth-bore drive sprocket I bought from McMaster, I had to mill a flat on the shaft to convert it into a D-shaft.


Afterwards, it looked like this:


Next was similar treatment for the sprockets themselves - I drilled out the 1/4" bore to 8mm to match the 8mm shaft of the CIM motors, and I drilled and tapped a set screw hole.


And here it is fixed on the motor shaft:


Next, I waterjet out the preliminary mounting brackets, cut some more 80/20 lengths, and just started throwing screws into things.


Because I had designed (and actually cut out, which was a mistake) the aluminum sprockets prior to getting my Harbor Freight wheels shipped in, I estimated the hole pattern as a 2.5" diameter when in fact it's actually 2.6". Accordingly, there was a bit of this involved:


As the waterjet can only cut parts in 2D, I also had to chamfer the edges of the sprocket to allow the chain edges to slide over it. I just eyeballed this one on the MITERS lathe.


Next, I had to part and tap a bunch of little spacer doohickeys to attach the sprockets to the wheels.


I also made some delrin spacers to axially support my wheel on one side, they have a chunk milled out of them so they can slide under the bar of 80/20 that the axle mounts are attached to.


And a shot of the part in action:


The chain was pretty sticky on my waterjet sprockets, so I went ahead and tensioned them and ran them in for 15-20 minutes each, just to get the chain to grind down the interfering aluminum on the sprocket.


Somewhere along the line, boredom and lack of a front half of the vehicle led to a rather monstrous contraption, with the combat robot Null Hypothesis serving solely as a temporary motor controller:


Then I waited a couple weeks and focused on Blizcopter while I waited for parts to ship in. I also made another giant waterjetting run, resulting in a pretty massive pile of parts.


I had to make some real tiny Delrin bore adapters for my steering knuckles, which honestly scared me a bit to turn because of how thin/fragile they were.

Yikes.
My waterjet holes on the front of the steering knuckles also didn't quite fit the 5/8" shaft I was using because it was about .005" over diameter, coupled with the fact that waterjet holes end up a little smaller than designed. So, naturally, the solution is to find a pointy 5/8" thing and just beast the hole wider (the shop I was in at the time definitely doesn't carry 5/8" drill bits).


And here's what the steering knuckles look like when mounted on the frame:


I then hack-sawed some threaded rod to put in the tie rods driving the steering linkage, and mounted front wheels.


Unfortunately the rest of my 5/8" stock didn't quite fit inside the bearings I'd bought for my steering linkage, so I had to move to the GIANT lathe downstairs and turn down the whole thing by just a few thousandths.

Seriously, this thing is massive. That shaft is easily 2.5' long.
Now the basic frame was close to complete! Obligatory celebration dance:

As you can see, the wheelbase is pretty miniscule.
Next, I needed a lot of T-plates to set up the attachment for the seat to the frame, and I didn't have enough patience to go waterjet these. So, I enlisted the help of a friend, and we started beasting out mount plates by hand. I started by cutting out some squares, clamping them together, tracing out the outline of the part, marking holes, and then drilling them out:


Next I bandsawed off the excess.


I went ahead and drilled some holes through the bottom of the seat and attached them to the tops of some 80/20 mounted to the brackets I'd just made. I also threw the steering 'wheel' (lol) on top of the steering rod, but it wasn't fully mounted at this point.


The seat was super wobbly in left/right bending, so I just attached a giant plate of 1/4" polycarbonate (yeah, that bullet-proof glass stuff) to the back to support it better. I also lasercut some quick acrylic battery mounts, and got started on wiring up the electronics.

Shh, don't tell the shop guys I did this...
I fit a laser-cut acrylic electronics mount into the center of the kart, where it holds the motor controller and ignition key.



I went ahead and installed some requisite glow-lighting, finished up the wiring, and was left with a reasonably functional go-kart:


Done? Not quite, there are still brakes to worry about. I cut out a cute little brake-pedal-mount-box, accidentally failed the clearance on the sides a little bit, and milled them out a touch.


I turned some little spacers to go between the brake discs and my sprockets, drilled more holes in the sprockets (same process as last time) and attached them together:


I still had some slight clearance problems with the brake calipers, so I had to dremel the face down just a little bit.


And a close-up showing the brake caliper mounted, as well as exactly where it was hitting the chain:


I also finished the brake pedal and added that to the front.


So, the state the kart is currently in:


And of course, obligatory underglow picture.



As you can see, the original front bumper I designed hasn't been fabricated yet because it's not essential to the function of the kart, and I haven't gotten a chance to go waterjet the mounts for it yet.

I decided to dub this kart the SmartKart, owing to its short wheelbase and passing resemblance to the similarly named car. The top speed is a calculated ~33mph, unfortunately the range is rather lacking as it only has two batteries, so it's only able to run for a continuous 5-10 minutes before running out of battery. Overall I'm pretty happy with how the project came out, and I'm looking forward to 'back-modding' it a bit now that my original design constraints (pretending to be in 2.00gokart) have been alleviated. It's not the most practical project, but it's a lot of fun to joyride in, and the tight turning radius makes drifting pretty easy. Now that the weather's picking up, I'll try to take it garaging sometime soon and update this post with a bit of video.

I've been pretty busy with classes this semester, which is why SmartKart took me so long to write up (it was effectively done a couple of months ago), but hopefully I'll be able to write another post soon (BE3P is way overdue for an update).

With SmartKart finished, the real 2.00gokart section started this semester, and I'm tagging along as a TA for the section and helping other students go through this process on their own. I'm hoping for some pretty crazy ideas/execution from them, and if I can manage to talk any of them into writing up their kart builds on blogs, I'll be sure to put up links here.