Buckle up those ESD wrist straps and comb the crumbs out of your neckbeards*, it’s hardware time. Some background before I go deep! I have a degree in electronic and electronic engineering, and a decade or so experience in industry, most recently designing weird in house test equipment for a consumer electronics company. I have designed so many PCBs I sometimes see KiCAD when I close my eyes, and sometimes (rarely) my boards even works on revision 1. All that is to say, there’s absolutely zero way I was building a combat robot and not going the extra mile and putting my own electronics in there! This post is half explaining my design choices, half explaining how I go through the process of designing a board.
*I’m only allowed to say this because I haven’t trimmed my beard in weeks, and I’m starting to look like someone who lives in a cave in the Appalachian mountains and only drinks moonshine and eats raw deer meat.
This board handles everything electronic inside Fatal Deviation that’s not inside the receiver or the weapon servo. It takes the channel data from the ibus receiver, combines it with the data from the IMU, and calculates how much power each motor requires to go in the direction you’ve asked it to. It also provides power and signal to the weapon servo, and of course RGB underglow.
This is the top level schematic. It’s arranged into sections - power top left, drive bottom left etc - if you zoom in they’re labelled. The main parts are:
- Seeed Xiao RP2040 microcontroller
- 4x TI DRV8526 brushed motor controllers
- PCA9685 I2C PWM controller/IO expander
- MPU-6050 IMU
- ADS-1115 ADC
- 7x WS2812B (aka Neopixels) in tiny 0807 SMD format
Most of the parts were chosen purely due to my familiarity with them from previous projects. The only exception was the DRV8526 motor drivers. I was aiming for approximately 6A of drive current per channel, as while the Valkyrie motors have a 14A stall current rating, a heavily loaded wheel on a bench top power supply might only draw 4-5A maximum, and ~500mA unloaded. Keeping stall current regulated to just a little above normal operating current means the driver can detect stalls and kill power before the brushes and windings cook themselves. In this application there’s no benefit to running the full stall current - drive wheels usually spin way before they lock up, and ifthey’re truly locked up (jammed against a wall or in a grabber) you don’t want to give them full power. Stall current is, however, something you’d 100% be thinking about for a lifter/grabber motor.
The PCA9685 PWM driver was used because the Seeed Xiao board doesn’t have enough GPIO pins to control all the motor driver functionality. A future revision of the board might eliminate it by switching to a bare RP2040 (which has enough pins, more than the Seeed form factor can handle), but the ease of soldering on a £2.50 dev board wins out for now.
When I draw a schematic I usually start with the power section. In this case there’s not much going on. Some pads to connect to the battery loom (and fuse). Some pads for a link that I ended up not using. Some indicator LEDs, bulk capacitance, and a 5V linear regulator to power the Xiao and RGB underglow. There’s also a voltage divider for battery voltage, with a filtering cap and a diode to 3V3. The diode is a belt and braces clamping diode that stops the voltage on that line from exceeding 3.3V plus the forward voltage of the diode. It shouldn’t, but sometimes weird stuff happens.
Clockwise it’s the MCU, IMU, and RGB. Nothing much going on here either - I just copied the application schematic for the IMU. The MCU has basically everything it needs onboard already, but I added some extra capacitance and a series diode. There’s also a ferrite bead specced, but I’ve been fitting a 0 ohm resistor instead without issue. The series diode prevents the rest of the board from being powered while programming, and also prevents the USB 5V rail arguing with the 5V linear regulator over who’s boss. Speaking of linear regulators, the RGB on this board is actually thermally limited to about 50% brightness by the temperature of the linear regulator, which is disappointing.
The motor driver sections of the schematic are enclosed in hierarchical sheets - this is because it’s just the same section copy and pasted four times, and using a sheet allows you to use the same schematic as a reference for multiple blocks. In this instance it’s essentially the datasheet application schematic (always a good start when designing something!), with the addition of some high frequency decoupling capacitors on the motor pins (C41,69,73), a diode on the fault pin, and a low pass filter on the current shunt.
The full set of decoupling caps are pretty extra, usually you can get by with just a ceramic cap across the motor terminals on the can, and/or fitting just C41 while omitting C69 and C71, but seeing that Fatal Deviation is a radio controlled steel box with no room for a receiver antenna I wanted to minimise EMI where possible.
The diode’s part number is “JB”, standing for “jellybean”. I didn’t have enough MCU pins to be able to connect all the fault pins their own inputs, so I OR them together with diodes and use it to check overall drive status instead of individual axles. I haven’t actually used this feature yet, I’m happy just having faults reset themselves automatically. If you ever see me limp the robot to the opposite side of the arena and pause for 1-2 seconds before resuming, I’m waiting for the 1s reset timeout to see if it fixes anything!
The LPF on the current shunt allows me to ignore the effect of the motor PWM frequency on the current readings. Without the LPF the ADC will either read full, short circuit winding current (when the PWM cycle is on), or no current (when the PWM cycle is off). While there’s benefits to taking a tonne of samples throught the PWM cycle and doing the filtering in software, in circumstances like this an analogue filter works just fine to smooth the pulses out to an average level. I read the current shunt voltages directly with an ADC1115, which has internal amplifiers with up to 32x gain, meaning I don’t need any external circuitry to measure the 0-60 millivolts or so across the shunts.
So far, everything is pretty standard, straight out of the datasheet stuff. It probably took me 5-10 hours to design the schematic, including part selection and a little bit of Spreadsheet Time for calculating component values and such. The PCB, on the other hand, probably has 40 hours behind it. Laying out and routing PCBs (especially >2 layers) is like piloting the Windows 95 Pipes screensaver and I find it extremely meditative, very easy to get lost in. I figure if I’m going to spend the time it might as well make it count, so I spend a lot of time shuffling things around to prioritise things like symmetry, aligning and spacing of components and traces, and using the absolute lowest number of vias possible.
The first step to laying out a PCB is usually the mechanical layer; either defining the space the board has to fit into, or roughly defining the board size required to fit the design into (depending on how critical form factor is). For such a tightly integrated design as FD there’s an amount of chicken and egg involved; exporting the board shape as a STEP file into freeCAD, then shuffling the board around in space while also changing its dimensions, until finally it fits into the space left inside the robot. I settled on the shape above, which is almost the full width of the robot interior, and half the length, except for cutouts for gearbox clearance.
Once the overall dimensions are specified it’s best to figure out rough locations for things like connectors and any components with mechanical constraints or requirements. In this case it meant placing the MCU (with its exposed USB-C connector) and some power and signal pads. I also placed a single M4 screw hole to soft-mount the PCB into the TPU guts of the robot with an M3 screw. It’s otherwise retained by just pressure and EPDM foam, allowing the board and robot to flex and shift independently of each other.
The board uses a four layer stackup. From top to bottom these are loosely assigned as signal, GND, PWR, signal. Aside from the power plane, which has a 5V zone fill, all the layers use a ground zone fill by default. The second layer’s ground plane is left uninterrupted* across the whole board. Putting the power and ground fills on the internal layers (which are physically closer together) gives you some free capacitance for high frequency decoupling. Exposing big external board-wide power fills to the outside world is also generally a bad idea, especially inside a metal robot, as it massively increases the chances of a short.
*Mostly, we’ll get to that.
The next step is to define the “most critical” portions of the circuit to lay out first, so that less important things can be laid out around it. The focus here is to get power from the battery and out to the motors as efficiently as possible, so it makes sense to put the motor drivers down first, and do it close to the power input. For this section I used a kicad plugin for generating PCB layouts for hierarchical sheets, so you only have to lay it out the one time. Thankfully I was able to fit all four drivers perfectly along the required edge.
Supplying power to the motor driver ICs takes some work. According to the kicad trace width calculator, to carry 6A down a 50mm trace with 20 degrees of permissible temperature rise requires a 2.3mm wide trace. 12A requires 6mm! You can reduce these by half by choosing 70um thickness copper (default is usually 35), but; the widest trace we can get into a pad on the tiny SMD motor drivers is still only 0.25mm, and choosing thicker copper is usually expensive, especially on >2 layer boards. The solution is (again) zones. It’s easiest to see from the bottom face of the board, where battery + enters the board, passes through the unused link pads (bridged with a wire on these boards), and into a large zone that spans the full width of the board. This exists in some form on every layer, and provides a large, low resistance, low inductance connection for the motor drivers. Now each 0.25mm wide power pad has a small area of decoupled (at least 10uf and 100n per power pin) copper zone, each connected to the contiguous zones below with lots of via stitching. Vias have high inductance, so using a decent number to tie zones together is a good idea.
The area on the top side of the board between the motor drivers contains the board’s bulk capacitance, which means I simply placed as many 1206 10uf capacitors as I could fit in the space available. When board space is constrained, spending the time to work out a tidy low inductance design can make more difference than trying to squeeze in more capacitors in less than optimal places. Also, I don’t have the Z height for larger capacitors, and I found this reel of 1206 10uf caps in a bin, so I might as well use them up. I haven’t seen any issues with voltage sag, a full power launch only causes a few hundred millivolts of battery/wiring/fuse/connector voltage drop.
Inner ground layer only, ground plane highlighted.
All layers except inner ground layer, ground plane highlighted.
Remember what I said about the uninterrupted ground plane? I lied, there’s actually some motor driver power zones on that layer too. These zones break up the big continuous ground plane, so that there’s a clearly defined “big power” side and a clearly defined “idk MCU stuff” side. The only place they meet is under the linear regulator and ADC - this keeps the return currents in check, because we don’t want the power that returns to battery ground from the motor drivers to cause issues with the sensitive side of the circuit.
Return currents from the drivers need to be considered as much as the supply currents. A big chunky power feed is great, but if the power has to return to ground through a tiny sliver of copper and zig zag around components it can cause differences in ground potential across different parts of the ground plane. This can manifest anywhere from weird noise on measurements to (in extreme cases) burning up narrow sections of ground plane. We always want to provide enough copper for the current to get back to ground, and (like I’ve tried to do here) it’s sometimes helpful to use physical cutouts in ground planes to isolate these fluctuations away from sensitive ADCs and IMUs to places they won’t matter, like underneath the motor drivers. This is a variation on “star grounding”, which is also good practice for wiring your robot that uses normal robot parts!
With the power sections laid out, everything else goes around them. I was pleased that I could get the IMU, PWM driver, ADC and linear regulator all down the centre line of the board to keep it as symmetrical as possible. RGB and some decoupling capacitors went on the back.
All this time, I haven’t been routing (drawing traces). Once you start routing traces, moving and modifying the design gets a lot harder, so I’ve been dragging components around trying to optimise the rats-nest of connections that should be connected, and only when I’m sure the board is close to final I will start drawing. I have been known to get half way through and scrap the design so that I can start again and do it better.
Once the board is routed, I rubber-duck debug it. I go through the schematic and the datasheet and double check everything I’ve laid down is doing what it’s supposed to. I’ll run the design rule checker to check I’ve not done anything really dumb. Once I’m sure I’ve checked myself I send the files off to one of the big fab houses in China for them to work their magic and wait a few weeks. This is usually about when you start finding bugs.
If I remembered to buy a stencil with the boards, assembly is a case of smearing solder paste through the stencil onto the board, dropping the components on one by one with tweezers, and putting it on a hotplate to reflow the paste. I usually have to clean up some joints with an iron and flux afterwards, but that leaves me with a complete board ready to be programmed and go into the robot.
I think that covers most of the design of this board. Fundamentally a microcontroller with four brushed motor drivers and some sensors and comms isn’t too off the wall, but getting it all to fit inside a tiny robot and not immediately catch fire takes some thought! I’m going to leave it there - stay tuned and keep your pocket protectors ready for the next episode, where I go into the firmware architecture.
Edit: bonus GIF of Fatal Deviation returning to its home planet (it was needed), credit to JoeB






