Browser-Controlled Tracked Robot

A tracked vehicle for playing around with. Controlled from laptop for ubiqutousness, educational value and ... uh... data.

Similar projects worth following
I've always wanted a tracked robot to play with. I could always buy one. But that would be too easy. So I'll make one. And just for kicks, I'll make it browser-controlled. And to keep it from being too easy, I'll make the motor control board myself.

There are four main components:

1. The control board (that I'm making myself)

2. A Tamiya tank chassis and gearbox

3. A raspberry Pi with web server and fcgi API

1. The control board

Going on Version 7 (Minified3).

MCU used is AT32UC3L0.

Using separate battery packs for motors and everything else. Motors need 6V (4 x AA). Everything else uses 12V (8 x AA).

2. Chassis

A basic Tamiya model. But was fitted with 6V motors with 800mA stall current (each). You can find those from Adafruit. The smaller current apparently keeps any radio interference to a minimum too.

Notice that I used two chassis kits. Not enough plastic sheets in just one.

3. Raspberry Pi

I used the fcgi-lib to write a minimal RESTful API. Also a minimal web-page to control the contraption.

Server back-end is Nginx, because it was the first one I found clear fcgi-usage instructions for.

Physical layer is a Wi-Fi dongle. I'll try and put the RasPi in access point mode, so that only a laptop will be needed for remote control. For now, it needs a separate access point.

Sources and licencing:

All code (and instructions) is in GitHub. See the README in there.

License on all of my code is GPLv2, i.e. open software.

Other licenses that may apply have to do with the Atmel driver libraries for the MCU, but I'm rather confident that those're open for everyone to see and freely (re-)distributable.

  • 1 × Raspberry Pi or AriettaG25 Server
  • 1 × WiFi dongle (for Arietta get the Acme module)
  • 2 × Tamiya Tank chassis kit
  • 1 × Tamiya tank tread kit
  • 1 × Tamiya dual geabox kit

View all 8 components

  • Version... sigh... 7

    OldCrow02/22/2016 at 19:31 0 comments

    Version 7 of the PCB. And still issues.

    Apparently I soldered the current measurement resistor on badly. The motor driver ground level shifts too much when the motors are driven. Kills theMCU.

    I'm back to using the AT32UC3L016. Mainly because it has PWM on every pin. I thought that this would make it easier for me to do the PCB traces. Yeah, no. I also misread the clocking options on the data sheet, so this design is now limited to a 16MHz clock.

    The programming interface this time around is the Atmel-proprietary 1-pin protocol. You're going to need a JTAGICE3 or similar to program it if you're going to try this design. Also, as a result of the Atmel merger, the AT32 series is probably on it's way out the door. Support was minimal as it was; now it's going for good.

    On the positive side, going to AriettaG25 for the Linux board gives a... compact result. Of course, Raspberry Pi Zero will work just as well if you don't need the second USB port for the camera. Arietta has 2 USB ports available straight away. They set the third to USB slave mode in the kernel driver, from the looks of it.

    The best part of this post though? The mechanics. I figured them out. ...No? Well, I was happy with them for a moment...

    P.S. I updated the GitHub project. It's now cleaner and has instructions in the README.txt .

  • V4 published to GitHub

    OldCrow07/07/2015 at 07:42 0 comments

    The 4th version is now published. You never saw version 3. Or version 1. But never fear, for those were dismal failures not worthy of your attention.

    In this version there are 2 separate battery packs. Why? Because the 130 size motors were a pain with more than 6 volts. Also, now I don't have to worry about isolating the CPU side from the motor's power line effects.

    I read from somewhere online that RC hobby servos usually work down to 3.5V and can accept 3.3V control signals. And here I've been wasting time and energy to put 5V level conversion just for servo control! What a waste. While I'm at it, I might as well remove the 5V regulator from the servos and just connect them to the motor's battery pack. Since I have servos only for the camera, they're not heavily loaded and should be happy with 3-4 AA cells.

    That'll also get the servos to the motor-side battery pack and I'll have one less source of interference. Funny how simplification works.

    But that's still a bit in the future. Right now, the v4 still has the level shifters and regulator for servo control.

    Note to self: Make silk-screen text thicker. It doesn't seem to survive the fab process too well at current thickness.

  • Code added to GitHub

    OldCrow03/14/2015 at 19:30 0 comments

    I've added a new picture and added all the code to GitHub, so that anyone may now replicate my results.

    I received the PCBs for the optocoupler from China already. And I orderd the parts from Farnell. Unfortunately, I'd picked a part for the 1-gate buffer that they turned out not to have in stock. It's in backorder.

    No, I'm not going to wait for 6 months for the part to come in stock again. I have another PCB coming from China already. This one will just break out a MicroUSB connector's pins so that I can solder as thick a ground cable in there as it needs.

    EDIT: P.S. The new photo isn't just for vanity's sake. I switched battery holders to be able to move the battery much lower. Despite how it looks, it should be much more stable now.

  • Grounding issue with Raspberry Pi

    OldCrow01/13/2015 at 17:35 0 comments

    So I got the server part working a-OK on the PC. OK.

    And I got the Control Board to spin the motors through browser commands. OK.

    And I got the service running on he Pi. OK.

    But as soon as I connected the the Control Board and the Pi, the Control Board goes nuts.

    And when I tried again, it stayed alive only until I connected the Cat5 (I'm planning to debug via Ethernet before going Wi-Fi).

    The problem

    I spoke with a friend with more experience. His opinion is that there is a grounding issue, caused by the thin USB cable power wires. See the picture attached.

    Since the USB GND wire is so thin, the my MCU would actually create a different ground potential and thus apparently pull a bit too much current in the wrong place. Or so we think.

    The Pi's arrangement of only having a Micro-USB connector for power input is, well, bad. There just aren't many Micro-USB cables to be found that would have a reasonably sized ground wire. Or maybe I'm just unlucky.

    The fix

    Since this is likely not the last bug with this board, I'm not going to make a new board yet. Instead, I'm going to add a smaller board that will take the ground-signal interaction further away from the ControlBoard and, more crucially, away from the AT32 MCU's own pin.

    This could be done in a variety of ways. For example, I could just add a pair of buffers somewhere close to the Pi's power input and wire those to suitably beefy power cables. But this would be a one-off board with no other uses. And when the minimum quantity of PCB orders from China is 5 or 10 pieces, I don't want that.

    Instead, I'm going to make an optocoupler board. That will have a variety of uses even after this project is over and done with. Or rather, I've already designed the board in Eagle and sent it off to the fabhouse. I'll update again when they come.

    I'll make a separate project page for the OptoCoupler board soon.

  • Both motors run, controllable from UART

    OldCrow11/28/2014 at 17:36 0 comments


    90% of components now attached to the controller board. And the rest may not get populated at all, as they seem to be largely unnecessary for the time being. I'd imagined that more servoes could be needed. Adding them later is easy though; I just need to solder on the headers. No software changes necessary.

    Finished the class for driving motors with the timer channels and PWM. I'm beginning to think that the choise of MCU paid off after all. I'm using 2 timer channels for motor control, 1 for the servos, and I need 1 for timing periodic tasks and communication timeouts. That leaves 2 timer channels free. Plus simple PWM on any pin if I feel like it.

    Added a new picture to the gallery. But I should add it here too... I'll just resize it so it's not as humongous.


    I put 100nF ceramic caps on both motors between the leads and the casing. This should keep the electrical noise to a minimum. But if I ever notice any interference, I still have some ferrite rings I can put on it.

    NOTE: If you put all 3 capacitors on a DC motor like I have, remember to make a connection from the casing to the ground. Otherwise the 2 caps from the leads to the casing will do nothing. The ground connection is the black wire in the photo.

    Other functions

    Servos move on command too. Channel 1 had a small bug. NOTE: If someone took my earlier published code in use, servo channel 1 will not work. I'll see if I can edit in a fix.

    A led can be blinked remotely. The first thing I actually implemented, as it's the least likely to break something if I get the code wrong. The worst a red LED can do is burn 5mA of current. So if you ever build a communications link to something that will affect a physical action, try using it on a LED first and leave the other functions commented out until the LED works flawlessly. Trust me, you don't want to test a comms link on an electric motor; your project may run out of control if you do.

  • Turnigy servos have a continuous rotation mode?

    OldCrow10/18/2014 at 14:57 0 comments

    Things I learned today:

    -If nothing seems to work, check that you're toggling the right pin.

    -Always double-check your timers' clock source and -rate.

    -If the servo PWM signal is outside the 1-2 ms area, Turnigy servos will rotate continuously. Or at least the TGY-1800A model did. And seemed unharmed afterwards.

    Dear diary,

    I sent the last 4 hours scratching my head because one of the sercvo control pins on the MCU didn't seem to change states. After I finally realized that I could just add to the debugger's watch window the whole class, I realized that both pin number variables (Clk and Rst) had the same number. Traced that to a wrong assignment in the initializer.

    After that was sorted out, I realized that the servo I'd connected to the board was rotating continuously at times. I'd set my test code to move all servos approximately 30 degrees every 2 seconds, for 12 seconds total, and then start over from the other extreme. Since this is servos we're talking about, with a PWM control signal, the pulse length was the obvious culprit. And so I checked and double-checked the stated values written to the timer's registers. But those were all in the correct range. Then I observed what the (presumed) pulse width was when it entered continuous rotation. It was around 1.25ms. This had me checking for an off-by-half mistakes in the clock source.

    The AVR32 MCU I have here does not sport a prescaler as such. Instead, it has 5 clock inputs that are f32KHzRC, fFB/2 .. fFB/128. The fFB is the speed of the high-speed bus, the same as mu fCPU, which is 10MHz. I'd of course picked the fastest clock signal available. And promptly forgot, a week later when I wrote the code to use it, that it wasn't the 10Mhz of the fCPU, but fFB/2 = fCPU/2 = 10MHz/2 = 5MHz.

    Facepalm time. I was producing servo pulses between 2.0ms and 4.0ms. Yes, that would produce unspecified behavior.

    I still couldn't be bothered to put my code anywhere online, so here's the servo class:

    #include "Servo4017.h"
    #include <tc.h>
    #include <gpio.h>
    void Servo4017::init(uint16_t gpioPinRst, uint16_t gpioPinClk, volatile avr32_tc_t *timer, uint16_t timerChannel, uint8_t pinsInverted)
        // Set the initial position to be at 50% of range.
        // That is, start centered.
        for(uint16_t i = 0; i < numServoChannels; i++)
            mServoPositions[i] = 32768;
        mServoNum = 0;
        mClkPin = gpioPinClk;
        mRstPin = gpioPinRst;
        mPinsInverted = pinsInverted;
        pmTimer = timer;
        mTimerChannel = timerChannel;
        // Initialize to the end of the cycle with reset high and clock low.
        mCycleTimeLeft = 1000;
        mPhase = 3;
        // Take into account the possibly reversed state.
        if(mPinsInverted == 0)
            gpio_configure_pin(mRstPin, (GPIO_DIR_OUTPUT | GPIO_INIT_HIGH));
            gpio_configure_pin(mClkPin, (GPIO_DIR_OUTPUT | GPIO_INIT_LOW));
            gpio_configure_pin(mRstPin, (GPIO_DIR_OUTPUT | GPIO_INIT_LOW));
            gpio_configure_pin(mClkPin, (GPIO_DIR_OUTPUT | GPIO_INIT_HIGH));
        // TODO: Hardware init
        tc_waveform_opt_t tcOpts; = mTimerChannel;
        tcOpts.bswtrg = TC_EVT_EFFECT_NOOP;
        tcOpts.beevt = TC_EVT_EFFECT_NOOP;
        tcOpts.bcpc = TC_EVT_EFFECT_NOOP;
        tcOpts.bcpb = TC_EVT_EFFECT_NOOP;
        tcOpts.aswtrg = TC_EVT_EFFECT_NOOP;
        tcOpts.aeevt = TC_EVT_EFFECT_NOOP;
        tcOpts.acpc = TC_EVT_EFFECT_NOOP;
        tcOpts.acpa = TC_EVT_EFFECT_NOOP;
        tcOpts.wavsel = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER;
        tcOpts.enetrg = false;
        tcOpts.eevt = TC_EXT_EVENT_SEL_XC0_OUTPUT;
        tcOpts.eevtedg = TC_SEL_NO_EDGE;
        tcOpts.cpcdis = false;  // Don't disable on RC compare
        tcOpts.cpcstop = false; // Don't stop on RC compare
        tcOpts.burst = TC_BURST_NOT_GATED;
        tcOpts.clki = TC_CLOCK_RISING_EDGE;
        tcOpts.tcclks = TC_CLOCK_SOURCE_TC2; // = PBAclock / 2
        // Init timer 1 for servo and second-counter duty.
        int resp = tc_init_waveform (pmTimer, &tcOpts);
        tc_interrupt_t tcInts;
    Read more »

  • C++ on AVR32 with Atmel Studio 6 is a pain

    OldCrow10/17/2014 at 20:22 0 comments

    Dear diary. Remind me not to try "paths less trodden" quite so often.

    I spent two evenings this week just trying to figure out why the binary size went from 3k to 50k. The only difference between before and after was my adding an instance of the freshly finished Servo4017 class to the SW.

    Looking through the .map file I noticed that a lot of standard library functions, e.g. malloc, were getting linked in. Which I considered weird, since the only instance I'd added was definitely static. I started to remove anything that might cause it. I don't remember the exact order in which I bisected the class, but the problem disappeared immediately after I removed the destructor. Ah, ha...? And WTF...

    So, lesson learned: don't be the first or the last to go somewhere. The first ones won't get paved roads, and the last ones usually don't come back with all their body parts.


    Adding a destructor to a C++ class in Atmel Studio causes it to link in half the standard library, including all possible memory control functions.

    In other news, I soldered in the TSR-1 regulator for the servos and the 4017 IC itself. I didn't test the library yet though; I've been busy with real life and work. For the same reason you don't get a new photo. Well, I'm also not feeling like celebrating the addition of just a few more components. Let me reiterate: too much real life happening.

    And don't get fooled by the front page picture getting changed. It's not a new photo; I just shuffled the photos around to get a better one as the preview image. I'm not usually much for PR, but getting one jolly wrencher and... uh... one follower?... after a few months of the project being up usually means one of two things: Either my project has absolutely no interest to anyone ever. Or I make a crappy first impression. Now, I'm not Einstein, Spielberg or Spock, but I'm rather certain that it's still the second option. And the only way to make a first impression in the project listings is the picture. So... advice for those who may follow: Put a front page photo that has actual object in it. Bonus points if it also has PCBs, gears (or other power transmission mechanics) and/or wiring. Extra bonus for shiny objects and blue LEDs. Trust me on this one.

  • Boards arrived, assembly underway

    OldCrow10/01/2014 at 18:03 0 comments

    The boards actually arrived 2 weeks ago. I just couln't gather the willpower to write a log entry. Especially seeing as I already got dropped from any competition. But let's see this through anyway. I don't want to end up as a part of the statistics on projects abandoned here after not being selected.

    As you can see unless you're blind or browsing ascii-only, I've already started assembly. I like to do the build process in phases, with testing in between.

    1. Power input and CPU regulation. Because if I screw it up, I end up with burned components. Burnt components is bad, so testing power input and regulation before assembling further is recommended. Funny fact: I always thought that I should put a 100nF ceramic also on the output of a 1117 regulator. Turns out that I shouldn't, but it still hasn't burned anything, for which I shall say a prayer of thanks to my god.

    2. CPU and bypass caps. It has a lot of pins in a small space, so it's the easiest component to destroy with a solder bridge. Can also be destroyed by badly chosen capacitors due to the internal 3.3V->1.8V regulator that will get unstable with low-ESR bypass caps. Tested after soldering by connecting to JTAG and reading ID and voltage.

    3. Xtal and caps. Tested by programming CPU to use Xtal and sending out strings

    through UART to see that the frequency is as expected.

    4. Voltage and temperature measurement, and power cutoff FET and led. This stage is here really to test the cut-off functionality. OK, and allowing you to program your MCU to cut the power if the rest of your board sports an otherwise smoke-producing error.

    And that's about how far I've gotten by now. More when I get more soldering done. I solder slow and careful, so don't hold your breath.

    Lessons learned this time:

    Atmel Studio does not have an easy way to switch to a more lightweight C-library for C++ AVR32 projects. Using sprintf causes the full library to get linked, which takes about 20k of space and is way too much on the 32k of FLASH the AT32UC3L032 sports. 

    If such a library even exists for AVR32, of which I'm not so certain either. If anyone knows one way or another, leave me a comment. And instructions, please.

    In the meanwhile, time to roll my own integer-to-string functions.

  • Second revision of board almost done

    OldCrow08/24/2014 at 14:51 0 comments

    Don't you hate it when you have to put the work "almost" in your log?

    I updated the picture of the layout. You should be able to see now where I'm going with this.

    Board TODO:

    1. Attach the Board Power FET's control to an MCU I/O. Choose a good pin.

    2. Put solderstop-less polygon on the main battery and GND traces, so that I can reinforce those with copper strands after the board is fabbed.

    3. Add debug LEDs. 

    I know that blinking LEDs are bad for the eyesight and sanity of anyone later admiring my handiwork, but they make such wonderful debugging interfaces. ...Alternatively, I could add some more headers to any free pins. Headers conneced to spare I/O pins tend to buffer against screw-ups.

  • I found someone with a Youku account, so video

    OldCrow08/20/2014 at 19:22 0 comments

    I don't want to know why they have an account, but here's a link to the video required by the rules:

    Be warned that it contains absolutely nothing that wasn't written in the description already. In fact, I'm not sure if the video contains enough information to pass the rules check. But it's 22:21 here in my time zone and I didn't sleep enough last night either, so I think I'll just leave good enough alone.

    Now to wait for the weekend, and maybe I'll actually finish the PCB redesign.

View all 15 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates