• Half-Duplex Serial

    Radomir Dopieralski05/20/2016 at 07:02 0 comments

    It's been a while since I did something with this project, but recently I decided to try and find a better way to communicate with the servos than I²C. Turns out the half-duplex serial code for attiny85 works very well. With it I can:

    • use the existing 3-wire servo cable for connecting, as it only requires 1 wire,
    • reclaim the other PWM pin for the H-Bridge, as you only need one pin and can use any of them,
    • use a "standard" protocol, like the bioloid one,
    • still have all the servos addressable on a single bus,
    • have additional debugging over the serial line.

    Which all sounds really great. Unfortunately for now I only have "send a byte" and "receive a byte" functions -- turning that into proper protocol support will take some work. But it's one hurdle less.

    Below my testing rig, with PyBoard as the other end of the half-duplex serial. I used code from https://github.com/dhylands/bioloid3/blob/master/stm_uart_port.py for it, and there were some surprises, but in the end the attiny85 side works great.

  • I²C Communication

    Radomir Dopieralski01/03/2016 at 14:19 4 comments

    So I started with writing the code for this thing. For a start I decided there would just be three 2-byte registers: target position, current position and PWM value for the motor (including direction). The current position and the PWM value would be updated automatically at 50Hz. Later I would add code to calculate the PWM value from the current and target positions using PID, and probably would add some more registers to tune the PID, but this was it for now.

    The code for that seems to be relatively simple:

    // From https://github.com/rambo/TinyWire/
    #include <TinyWireS.h>
    const uint8_t I2C_ADDRESS = 0x9;
    const uint8_t POT_PIN = 4;
    const uint8_t DIR_PIN = 3;
    const uint8_t PWM_PIN = 1;
    // SDA_PIN = 0
    // SCL_PIN = 2
    volatile uint8_t index = 0;
    volatile union {
        uint8_t bytes[6];
        struct {
            uint16_t target;
            uint16_t position;
            int16_t motor;
        } regs;
    } regs = {};
    const size_t MAX_REGS = sizeof(regs);
    void request_event() {
        if (index >= MAX_REGS) {
            index = 0;
    void receive_event(uint8_t bytes) {
        if (bytes < 1 || bytes > 16)  {
        index = TinyWireS.receive();
        while (--bytes) {
            regs.bytes[index] = TinyWireS.receive();
            if (index >= MAX_REGS) {
                index = 0;
    void update_motor(int16_t motor) {
        if (motor < 0) {
            digitalWrite(DIR_PIN, HIGH);
            motor += 255;
        } else {
            digitalWrite(DIR_PIN, LOW);
        analogWrite(PWM_PIN, motor);
    void setup() {
    void loop() {
        regs.regs.position = analogRead(POT_PIN);

    I based it heavily on the I²C example from the library I'm using. After brief testing on a separate chip, I decided it's time to flash it onto the chip on board of my servo board. And here's the first problem: the MISO pin is connected to the H-bridge, which interferes with programming.

    After delicately desoldering the pin and putting some kapton tape under it, I succeeded in programming the chip, though. Now time to test it.

    I used a Raspberry Pi to talk to the servo, because it gives me a nice interactive Python console. Reading and writing individual bytes seemed to work well enough, but as soon as I tried anything more complex, trouble materialized. Turns out that Pi has a hardware bug that makes it pretty much useless for I²C with anything that does clock stretching. Allegedly there is a library that does I²C by bit-banging and thus works around the bug, but it doesn't seem to work for me.

    A separate problem is that the SMBus library has a little bit higher level commands than plain I²C, and those commands are difficult to implement on the ATTiny85 side, when I'm only allowed to send a single byte in response to requests, and the read buffer only has 16 bytes.

    I'm starting to seriously consider using some custom single-wire protocol similar to one used by the WS2812 addressable LEDs. This way I could also re-use the original cables...

  • PCBs Arrived

    Radomir Dopieralski01/02/2016 at 12:17 5 comments

    The printed circuit boards arrived during the holidays, but I was travelling and so they had to wait. I'm not sure if it's my sloppy design, or OSHPark, but the boards seem to be of lower quality than what I usually got from China-based PCB shops:

    But they seem to be fine electrically. For now I only soldered the H-bridge on one of them, and added wires for the microcontroller pins, so that I can make the motor spin one or the other direction manually. That seems to be working fine:

    The next step is to actually program the ATTiny85 and solder it in place, but I'm taking my time with that.

  • Third Time's the Charm

    Radomir Dopieralski12/16/2015 at 17:20 0 comments

    So the DRV8835 came some time ago, but I didn't really have the motivation to try. I did now, and it turns out to be way too small for my breakout boards or for dead-bugging. So instead I tried with a second DRV8833.

    This time I used a new breadboard and proper capacitors from the beginning. And what do you know, it worked!

    So now the next step -- a printed circuit board. I made a two-sided one that is small enough to replace the original one from the servo:

    I know, it's not pretty. But it should get the job done. I ordered it at OSHPark this time, because they really have competitive prices for this size: $0.85.

    I think I will also make and order a breakout board for that DRV8835 after all...

  • Nope

    Radomir Dopieralski10/21/2015 at 13:00 8 comments

    No matter what I try, I can't get that DRV8833 chip to work -- I get no output voltage on the motor. Either the chips I got are faulty, or I can't read the datasheet. Oh well.

    I ordered some DRV8835 chips now. They are smaller and the connections are simpler, maybe I will have more luck with those.

  • Breakouts

    Radomir Dopieralski10/21/2015 at 09:12 2 comments

    Now that the breakout boards arrived, I finally have proper conditions for experimenting. Check it out:

    The boards I got are quite universal -- they have 1.27mm pitch on one side, and 0.63mm on the other, and 28 pins, so I was able to fit both chips on one.

    I also added wires and connectors to the parts of the servo I'm keeping -- the motor and the pot.

    Now comes the boring part: programming this thing.

  • Testing the H-bridge

    Radomir Dopieralski10/03/2015 at 15:47 2 comments

    As I said before, I want to have the circuit designed and tested before I order the actual PCBs. So I need to first assemble it without a PCB and test it. Initially I assumed that I will need a breakout board for the H-bridge chip, which is in a HTSOP16 package, with 0.65mm pitch, but after some tries I managed to solder the needed wires to it dead-bug-style. It's not very reliable, but should be enough for testing.

    Unfortunately, either the chip is DOA, I killed it while soldering or that current sense pin has to be connected to ground... I tried to test that last theory, and broke off the chip's legs in an attempt to solder it. Sigh, I guess I will need that breakout board anyways.

  • First Steps

    Radomir Dopieralski10/03/2015 at 12:22 0 comments

    While I'm learning to program the ATtiny85 for #Nyan Board, I can also design the tiny PCB to go inside the servo. Obviously it has to be double-layered, so I will have to order it. Before I do, though, I want to have a solid design for it, and the circuit tested. I will need to get some tsop16 breakout boards for that, as that's what the h-bridge uses. Anyways, first board design:

    The two large pads at the top are for the motor, the three smaller ones in between -- for the potentiometer. The pins on the bottom are for power and I²C wires.