OpenServo V1
A new era of motor controllers!
Thanks to PCBWay for sponsoring some of the PCBs for this project!
Ever since I’ve been into robotics, I’ve been really interested by motor controllers like ODrive, which allow you to control the position, velocity, and often output torque of brushless motors (commonly used in drones) from an Arduino or Raspberry Pi. They’re super cool, and I’ve always really wanted a few, but they’re expensive and all are seriously overkill for my projects, supporting much higher currents and speeds than I actually need. Additionally, the motors themselves are more expensive than I’d like. Last summer, while working on my Quadruped and talking to people at my internship at ModalAI (to which I’m returning this year again!) I realized that such a system has been missing for small-scale robotics and motors at a low price. The few motors that exist with these capabilities are expensive, like the Dynamixel AX-12A, and aren’t open source. My next challenge was then obvious: to design a motor controller that could be easily integrated into cheap and standard hobby servos. Thus, OpenServo was born!
Over the course of about 6 months (school is great, but also sucks a little) I designed a small motor controller, a programming board, and a peripheral board to get the project up and running. In this post, I’m going to detail most of my design decisions, explain some of the testing that I’ve currently done, and ways that I will need to fix my board in revision 2(yes I’m going to need a second revision 😭). Thus, this post will be fully hardware. No software speak at all, for the most part!
PCB Design
I designed the printed circuit boards for this project using KiCad, and open source eda tool. I have used it for all my projects and it works great, especially version 6 - this was the first project that I used it on. As I said earlier, I had to design three boards: a main board (with the processor and some controllers), a programmer board, and a peripheral board. First, it makes sense to look at the requirements I set for the board:
- The board must have a processor that can easily handle multiple PID computation
- It must have a way of detecting the motor shaft position (by means of an encoder)
- It must have a way of measuring the temperature of the motor
- It must have a way of driving the motor, preferrably at different speeds
- It must have a way of storing configuration data (memory)
- It must have a way of measuring the motor current draw and bus voltage
Let’s tackle the first task: finding a suitable processor. I immediately gravitated towards the ATSAMD21 series of processors since they are fairly fast, very well supported by the Arduino environment, and are small. I ended up selecting the ATSAMD21G (the 48 pin version of the processor) since it was the only one in stock (there is a serious shortage of them right now). It has the right number of IO ports for me, including 2 SPI ports and 1 I2C; perfect!
Datasheet for the ATSAMD21G:
A consideration for this board was also that it needed to be small, meaning that I would need to make my own arduino, which is why I needed this processor at all. In order to make an arduino, however, it is necessary to be able to burn a bootloader onto it. I did this using a programmer board in order to save space on the main programmer board, which included spring-loaded pins (“pogo” pins) to make good contact from the programmer onto exposed pads on the main board. Then, I burned the bootloader onto the processor by connecting an SWD debugger to the programmer board (remember, all the connections are made through the programmer and into the main board, so everything is connected) and using OpenOCD to program it in. Earlier this year while studying the methods for doing this, I made a post that ended up being really important when I really burned the bootloader since I completely forgot how to use openocd. Moral: documentation is good!
The second task was to detect the motor shaft position using an encoder. This is important since the encoder inside common hobby motors are the only thing preventing them from spinning past 270° - most actually use a small, inaccurate potentiometer. I chose the AMS5600 absolute magnetic encoder because it was fairly cheap, had good resolution, and was an I2C device. It is also very small and easily fit into the existing motor casing with a couple 3d printed parts. The encoder was mounted to the peripheral PCB to be placed as close as possible to the motor shaft - end the end goal is to have a stackup of 2 PCBs inside the hobby motor. Note: This PCB was kindly sponsored by PCBWay and turned out great!
Third, I wanted to be able to measure the temperature of the motor. This was accomplished through the use of the PCT2075 I2C thermometer. Although not a critical part, I thought it could be fun to analyze the temperature of the motor with respect to the output torque or speed, or something like that. Its only practical purpose is to warn of high temperatures due to motor lockup (or something like that). I also chose to put this on the encoder peripheral board to save space on the main board.
Driving the DC motor turned out to be incredible easy though the use of the TB67H450, which takes in two PWM signals for each motor pin. This chip was super simple to implement - both in the hardware and the software - and is still overkill for the motor I’m using since it can deliver up to 3.5A and my motor only takes up to 750 mA.
Storing the OpenServo configuration was accomplished through the use of a flash chip, namely the incredibly popular GD25Q16C. This chip is well supported, has immense storage capability (compared to what I need, which is a few kilobytes at most) and is small.
Finally, I wanted OpenServo to have a method for measuring the motor current draw and bus voltage. For this, I used the INA219 current monitoring chip. This measured bus voltage well but seemed to mess up the current measurement… I’ll talk more about this later.
The main board was easily the hardest PCB I’ve ever needed to design. In order to keep costs low and to challenge myself a little, I forced myself to keep it a two-layer board. Don’t forget that the entire board is about 1.6x1.6 cm! I spent a lot of time on the design… some of my teachers may say a little too much :D.
Here is a picture of the final PCB design for the main board:
The peripheral board:
And the programmer board:
Here is a time lapse of me soldering all the chips onto the board:
And the final product (the peripheral board is already mounted inside the motor casing):
You can see the programmer board on the bottom with the USB plug and the main board sitting inside a 3d printed mount and clamped down with a toggle clamp. I really like this setup since it made it easy to remove the main board and to test everything together. Overall, I’m really happy with it!
Getting Started
After the hardware was finished, I decided to program the bootloader so that I could begin working with the board. I instantly ran into a weird problem in which the debugger appeared to not be able to connect to the chip properly:
zsh
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: FW Version = 1.10
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 400 kHz
Error: Error connecting DP: cannot read IDR
After about 5 hours of verifying the signal, power, and ground connections from the debugger to the programmer board and the programmer board to the main board, I realized that it wasn’t a disconnect issue at all… it was a too-much-connection issue, and the chip was constantly resetting. The samd21 processor resets when its reset line is pulled low, and I had an exposed jumper on the main board to enable resetting (the idea was that you could short the two sides together with a screwdriver to reset the board). As it turned out, I made a small mistake in my PCB design; the clearance on the reset pad of the jumper was too small to be manufactuered properly, about 0.02mm, causing a gound pour around it to merge into it. In other words, the jumper was doing nothing and the reset line was constantly being pulled low. After cutting the reset line trace, the bootloader burned successfully and the board showed up as a Serial port! Fortunately, I was able to continue resetting my board through a different line that led through my programmer board.
Here’s what the design looked like in KiCad:
And what the cut trace looks like:
Successes and Failures
After that I could easily program the board through VS Code’s PlatformIO extension (which is really nice by the way). Also, all of my sensors worked first try! The encoder could clearly measure accurate shaft positions of the motor, the thermometer worked, and the current sensor worked as well. Even the motor driver worked, and I was able to control the motor at different speeds. On the flash chip, however, I discovered that I accidentally flipped the MOSI and MISO lines 😭 but that was a minor issue that I fixed with a little more trace cutting.
The more important problem came a little later: the main board appeared to sporadically disconnect from my computer during uploads for prolonged periods of time. This meant that I could sometimes upload successfully to the board and open the Serial port, but most of the time (like 80% of the time, I’d say) the main board would be successfully uploaded to but immediately disconnect, or the board would disconnect during the upload and cause it to fail altogether. This was incredibly frustrating since It would take me up to 10 minutes to get one upload to work without any disconnects.
Initially, I believed this to be a power issue where VUSB would be dropping or the 3.3V regulator on the main board (the AP2202-3.3) wouldn’t be supplying enough current since I sometimes noticed using a voltmeter that the voltage on those lines would quickly drop during uploads. In an effort to fix this issue, I replaced the AP2202 with an AP2112K, which can output more current. This, however, failed; the board overheated and broke, and even after switching the regulator back, it continued to fail. To put it concisely, I broke my board. Tomorrow I’ll build a new one, and hopefully it’ll work? 😅
One more thing - the hope is to create a command line interface for this device… which already exists! I also made a documentation site for it, since that’s cool. The pip install isn’t live yet (since it’s still in the development phase and needs a few more upgrades), but check it out anyway!
Problems, for now:
Although this project is currently going much better than I expected it to go, there are a few problems that need to be addressed in later revisions:
- reset line clearance
- random port disonnects
- toggle switches instead of jumpers on the programmer board for switching power on / off
- fix a slight misalignment of the encoder on the peripheral board so that it fits in the motor enclosure better
Regardless, I’ve learned a lot from this project so far. It also totally revamped my KiCad skills, and I’m proud to say that I’ve started mixing up the KiCad and Fusion360 keyboard commands due to using both so much in conjunction.
That’s all… for now!
Update: I made a second board identical to the one that broke that appears to work completely - no upload failures, the current sensor works perfectly, and everything else is good. It seems as though the failures on the first board were simply a result of bad manufacturing and weren’t a design issue!
Thank you on more time, PCBWay, for sponsoring the programmer and peripheral boards (as well as their stencils) for this project!
I got a whole bunch of programmer and daughter boards to be used for this project. The best part is that I received them super quickly… superrrrr quickly! Within 5 days of ordering the boards, they arrived at my door. Unfortunately, I couldn’t use them for a while as I waited for the components to put on them!
I was also able to get them in matte black which looks really cool, although it would’ve been cooler if I had spent more time on the silkscreen for the boards.