close-circle
Close
0%
0%

Servo Breakout for WeMos D1 Mini

Connect servos to your ESP8266 dev board.

Similar projects worth following
close
Servo shields for the WeMos D1 Mini boards.

I thought I will try to make some shields for the WeMos D1 Mini, because it's one of my favorite ESP8266 boards. There is already an official motor controller shield (though I can't get it to work reliably), so I decided I will make some servo shields.

Buy it on Tindie.

wemos-servo8.zip

Gerbers for the 8-channel board.

Zip Archive - 170.95 kB - 08/23/2016 at 13:00

download-circle
Download

wemos-servo8.fzz

Fritzing design of the 8-channel board.

x-fritzing-fzz - 31.74 kB - 08/23/2016 at 13:00

download-circle
Download

  • Fabrication and Version 2.0

    Radomir Dopieralski12/04/2017 at 10:17 6 comments

    There is a relatively high demand for this board on Tindie, so when I sold the last of the 75 hand-assembled modules, I stated looking at fabrication options. Unfortunately, for a board with so many soldering points (the servo socket headers), it was relatively expensive. I didn't realize that the work I was doing by hand is worth so much. In any case, I asked several fabrication services for quotes, and since I didn't want to increase the price too much, went with the cheapest one, which turned out to be PCBWay.

    They were very helpful all along the way. The bill of materials CSV file I sent them didn't parse quite well for them (probably because I wrote it by hand in Vim), but they had no problem with me just including an ASCII-art table in the e-mail instead. On every step of the process they sent me photos, asking if it looks correct. They also tried to catch possible errors themselves: for example, my BOM didn't include the stacking headers that I add to every module — they saw them in the photo and asked if they need to be added. (I send them unsoldered, so I add them later myself). There was also one small glitch when they initially wanted to use straight pin headers instead of angled ones, but because they sent me the photo I could point out the mistake, and there was no problem switching to the correct headers.

    I choose the cheapest shipping (again to keep the price low), but it still arrived without problems in 2 weeks. Here's a photo of the package I got:

    The anti-static bags on the bottom contain the soldered panels (I choose to depanelize them myself, again to save costs). The rest of the bags are all the left-over parts, including one unsoldered PCB panel of 5 modules.

    A quick test shows that the modules work very well.

    All in all, I'm very happy with them. You can buy the new modules on Tindie here: https://www.tindie.com/products/deshipu/16-channel-servo-shield-for-d1-mini-version-20

  • Faster Assembly

    Radomir Dopieralski08/01/2017 at 20:14 0 comments

    I recently got myself a cheap hot air gun, and as a test for it, I assembled one panel of the servo controller boards.

    I must say that it really changes everything. While applying the solder paste is still a little slow without a template, soldering all the parts is a moment, and they magically snap into their correct positions. I had a few solder bridges due to too much solder paste in some places, but a solder suction tool took care of those easily.

    You can see on the photo the testing probes that I'm using to test all the shields. The result this time was 100% working.

    Soldering the pin headers was even faster, at least compared to the manual process I used before. How come I didn't get one of those hot air things earlier?

  • Assembling and Testing

    Radomir Dopieralski04/04/2017 at 11:34 0 comments

    The package with the chips finally arrived, so I can assemble the remaining boards. Soldering the TSSOP packages by hand is not terribly difficult, but it can still be tricky. So I'm not trusting myself, and apart from visual inspection I decided to also do at least basic testing of the shields. On the other hand, I have to ship them with the headers unsoldered, so I can't simply plug them in and see if they work. Fortunately, I have some of those testing probes lying around:

    With that, I can easily check that the devices show up in an i2c scan, and that they can wiggle the servo. It''s not a full test -- that would have taken way too much time -- but it does cover the most common failure modes. Since I'm only making a few dozens of them, and I can assemble them as they are being ordered, I don't think I need to make a more sophisticated testing rig just yet.

  • Version 1.0 of the 16-channel Shield on Sale

    Radomir Dopieralski03/24/2017 at 17:48 0 comments

    After the prototypes that I put on sale on Tindie sold within 2 days, I decided to try and make a larger number of the servo controller shields. I choose to do it with the 16-channel PCA9685 shields, because to be honest I don't trust my own C code enough to inflict it on unsuspecting people. In any case, you can now buy the production version of this on Tindie here:

    https://www.tindie.com/products/deshipu/16-channel-servo-shield-for-d1-mini-version-10/

    I ordered about 90 PCBs (the Elecrow's 10x10cm offer is the best bang for the buck), but so far I only had components to assemble 5 of them. More components ordered, and I will put them in stock as they arrive. Here's a photo of the ready PCBs:

    I also wrote proper documentation manual for this board, it's available here: http://16-channel-servo-shield-for-d1-mini.readthedocs.io/

  • Usage Examples

    Radomir Dopieralski03/01/2017 at 15:43 0 comments

    So here is example code you can use to communicate with the 18-channel and 20-channel servo controllers, for different environments that you can run on the ESP8266:

    MicroPython

    import ustruct
    from machine import I2C, Pin
    i2c = I2C(scl=Pin(5), sda=Pin(4))
    address = 0x10
    servo = 4
    position = 1500
    i2c.writeto_mem(address, servo, ustruct.pack("<H", position))
    

    NodeMCU

    address = 0x10
    servo = 4
    position = 1500
    sda = 2
    scl = 1
    i2c.setup(0, sda, scl, i2c.SLOW)
    i2c.start(0)
    i2c.address(0, address, i2c.TRANSMITTER)
    i2c.write(0, servo)
    i2c.write(0, bit.band(position, 0xff))
    i2c.write(0, bit.rshift(position, 8))
    i2c.stop()
    

    Arduino

    #include <Wire.h>
    void setup() {
        int address = 0x10;
        int servo = 4;
        int position = 1500;
        Wire.begin();
        Wire.beginTransmission(address);
        Wire.write(servo);
        Wire.write(position & 0xff);
        Wire.write(position >> 8);
        Wire.endTransmission();
    }
    void loop() {
    }
    

  • Selling Prototypes on Tindie

    Radomir Dopieralski02/25/2017 at 02:00 0 comments

    I decided to give Tindie a try and sell the prototypes of servo shields that I've built. I have three of each of the 16-channel and 20-channel ones. You can get them here: https://www.tindie.com/stores/deshipu/

    I'm basically just getting rid of them, since the project is complete and I don't really need so many servo controllers -- there are only so many robots I can build at once. On the other hand, it would be a waste to just throw them away or let them rot in my drawer -- so I'm selling them.

    While they are all manually tested, keep in mind that they are still prototypes -- purple PCBs, hand soldering, possible hidden bugs -- I will support them the best that I can, but I can't replace them (because I only have 3 of each and I'm selling them all) if they stop working. I can fix bugs in the firmware for the 20-channel shield, but then you will need an ISP programmer (or any Arduino, in fact) to re-program them -- I can help with that, but it does take some time and effort to setup. The 16-channel servo uses an off-the-shelf chip, so there are not likely to be any bugs.

    This is also an experiment in gauging the interest in this. If they get added to a lot of wishlists, I might consider making a larger batch of them.

  • PCA9685

    Radomir Dopieralski02/23/2017 at 15:56 1 comment

    @Jonathan Beri asked in the comments why I didn't use PCA9685, but instead went with an ATmega and my own code. To be honest, this is mostly because this project is the continuation of my #Servo Controller project, in which I used a Pro Mini board.

    But that got me thinking, and I went and checked if I can get some of those chips cheaply, and I went ahead and designed a PCB for them:

    I was actually so happy with it, that I went ahead and ordered them from OSHPark. Today the boards arrived, and I assembled one and tested it:

    It works perfectly fine. You only get 16 channels (that's why I could fit it on the standard D1 Mini shield), but you can stack 64 of those boards (the address selection jumpers are on the bottom). Pretty neat.

  • 20ch Firmware Improvements

    Radomir Dopieralski01/20/2017 at 21:25 0 comments

    Today I spent several hours working on the firmware -- adding the address selection support and making the whole thing much robust.

    The first thing I added, was to modify place where the servo events are updated. Until now, they were updated immediately after the I2C transaction finished -- as soon as we had the servo pulses. But that meant that if you are unlucky, it could happen while the timer interrupt fires, and the data seen by the interrupt would be inconsistent. We can't switch off the interrupts while updating the events, because it takes very long time, and our timings would be completely off then. So what can be done? Well, all the events typically happen in the first 2ms of the cycle. We have 18ms left to update the events (that's also why the glitch wasn't so easily visible). So now, when the I2C transaction finishes, a flag is set signalling that an update is needed. Then, while waiting for more I2C data, the loop checks that flag and whether the last event in the cycle has been already processed. As soon as the last even is processed (which we can recognize by the fact that it has delay of 0xffff), we update the events and clear the flag. Sounds easy, but it took me some hours to get right.

    Unfortunately, that didn't eliminate all the glitches. The second problem happens when you have two events very close to each other -- so close, that processing of the first one finishes after the second one's scheduled time. You could expect the second event to be a bit late then, but it's actually much worse -- since the interrupts fire on equality, and not whenever the counter overflows the trigger, the interrupt for the second event is skipped entirely and never fires -- the pin is only updated in the next event, or never, if that event was the last one. Ouch. I spent some time optimizing the interrupt routine and moving stuff in it to make it a bit more robust, but it's never processed instantly, and the problem remains. In the end, I'm just looking for events that happen too soon after the previous one, and when that happens, merge them with that previous event. It introduces a few microseconds of a glitch, but that's better than the alternative.

    For the address selection, I had to use the analog-only pins of the ATmegaXX8. I was already a bit tired when I started on it, so I stupidly copied the code from my #Mechatronic Ears project, forgetting that it's for an ATtiny85. And it mostly worked -- the AVR chips are very similar, after all, especially when using the correct header files with the right macros -- except that I masked wrong bits in the mux selection. It took me sever tries to track that down, but once I found it the fix was trivial.

    The next bug is related to the internal pullups I'm enabling on the I2C lines. Turns out I had the masks for the PWM routines slightly wrong, and they were also toggling the pullups. The effects on the I2C transmission were... interesting, if not a bit nondeterministic. It helped that I got a really nice logic analyzer for Christmas, that also has an analog channel, and that I could see the voltage levels on the I2C lines acting weird, even when there was no data being transmitted. Fixing this while keeping the interrupt routines as fast as possible took several tries, but I managed to move all the operations into the event update routines in the end.

    The last thing I added is sending a stop condition on the I2C bus whenever something unexpected happens on it. That lets me recover from errors in communication more easily.

  • 20ch Servo Shield Assembled

    Radomir Dopieralski01/20/2017 at 10:13 0 comments

    It took some time, but the 20-channel version of the D1 Mini servo shield is finally assembled. I still used an ATmega328p, because that's what I have on hand (the code is now small enough to work on an ATmega8). As you can see on the picture below, the angled headers make it stick out of the D1 Mini outline a little bit -- but now you can really stack them! Once I update the firmware to take into account the address selection jumpers, you will be able to stack four of those babies, giving you a total of 80 servos to control. Of course powering that many servos from the poor D1 Mini is a bad idea (unless most of them only move sometimes), so you can also cut the trace connecting the 5V power with the D1 Mini pin, and provide the power separately.

    Of course, as soon as I got the PCB in my hands, I immediately got some ideas on how to improve it: connect the reset pin to the D1 Mini's reset pin, for easier programming and to make them both reset together, make the D1 Mini outline on the top of the board smaller, so it's more readable, etc. -- but those are small things, and I'm mostly happy with this version.

  • Firmware for the 20-servo Shield

    Radomir Dopieralski12/19/2016 at 09:16 0 comments

    It took some work, and it took some compromises. I ended up not using the first trick I mentioned, of sending the signals for each port separately. I also ended up needing sorting anyways, but did that with a horrible O(n²) insertion sort -- anything to save some bytes. But it's here and it works. I even managed to squeeze in an array for remapping the channel numbers to what is actually written on the shield. The whole thing has 990 bytes and you can see it below, or in this repository: https://bitbucket.org/thesheep/d1-mini-20ch-servo/src

    Read more »

View all 20 project logs

  • 1
    Step 1
  • 2
    Step 2

    Populate the PCB -- solder the ATmega328p, the reset resistor (10kΩ), the I2C pullup resistors (10kΩ, optional), capacitor (10-1000µF, optional). Solder the pin headers for the servos and power, and an extra pin or wire for the reset.

  • 3
    Step 3

    Connect your ISP programmer to the 3v3, GND, MISO, MOSI, SCK and Reset pins as follows:

    If you are making the 2ch version, the pin names are printed on the PCB. Get the source code from https://bitbucket.org/thesheep/d1mini-18ch-servo/src for the 18-channel version or https://bitbucket.org/thesheep/d1-mini-20ch-servo/src for the 20-channel version. To flash the 18-channel version, use Arduino IDE. To flash the 20-channel version, run "make flash" in the firmware directory.

View all 5 instructions

Enjoy this project?

Share

Discussions

Thomas wrote 01/21/2017 at 07:58 point

Maybe I've worked too long in the field of electrical reliability to put too much trust in the mechanical fitness of a two rows of 8 pin headers to support the mass of an assembly of a rather dense assembly [shield-PCB+48 pin headers+16 3pin connectors+attached cables]. It might be advisable to provide mounting options that take away mechanical strain and stress from the pin headers. Also such an assembly easily draws a lot of current, even if no servo is ever stalled (0.2A x 16). The requirements of an adequate power supply (with sufficiently designed ground) might also be a good thing to be getting aware of.

  Are you sure? yes | no

Radomir Dopieralski wrote 01/21/2017 at 09:24 point

There is a connector for a separate power supply for the servos, and 99% of the bottom layer is a ground fill. As for mechanical ruggedness, you can always put the shields on the bottom, and the D1 Mini (which is relatively light) on top. But the shield is quite small and feels pretty solid. I agree it would be nice to have mounting holes, unfortunately there isn't much room left for that.

  Are you sure? yes | no

Thomas wrote 01/21/2017 at 15:13 point

You've got a point. I'm just not a big fan of "shields"  for different reasons.

By the way, an ESP-14 can control up to 15 servos without external controller. This should work even if the integrated STM8S003F3 has a 3.3V power supply.

Edit1 in response to @Radomir Dopieralsk: http://www.watterott.com/index.php?page=product&info=4831&dl_media=6623&x0993b=33c134530245f3b2921d85e84fa5d152

Edit2 in response to @Radomir Dopieralsk: if you need the ESP8266 serial interface for controlling the MicroPython console you'd be down to controlling just 13 servos (maybe 14 with some tricks).

  Are you sure? yes | no

Radomir Dopieralski wrote 01/21/2017 at 15:25 point

ESP-14? Is that another module with the ESP8266 on it? How does it do without an external controller, if you can only use 11 gpio pins on the ESP8266, and that's if you re-use the serial pins... Do you have a link?

  Are you sure? yes | no

Radomir Dopieralski wrote 01/21/2017 at 15:50 point

Ah, I see, so you program the stm8 chip inside. That's nice, but I want to use the esp8266 to run micropython on it, stm8 doesn't work for me.

  Are you sure? yes | no

Radomir Dopieralski wrote 01/21/2017 at 16:21 point

Just reply to the message one level above...

Looking at the pinout of the esp14 module, I don't see any esp8266 pins broken out -- so I don't think you can program the esp8266 on it, only the stm8.

  Are you sure? yes | no

Thomas wrote 01/21/2017 at 18:26 point

The ESP8266 can be programmed the usual way through pins 10 and 11 when pin 14 is low. The intent of the hardware design is to use the ESP8266 UART for communication with the STM8S, but it's also possible to use a simple serial protocol to transfer the data for servo control from E_GPIO0 to any STM8S GPIO.

Another option would be to make the STM8S listen to ESP8266 TX and filter out servo set commands from the data stream. There are a few more options. and depending on on-line console communication requirements, trade-offs and time spent on optimizations, anywhere from 13 to 17 servos can be controlled without using port expanders (like shift registers).

Please refer to the discussion here for another take on using the ESP-14: https://hackaday.io/project/16097-eforth-for-cheap-stm8s-value-line-gadgets#j-discussions-title

Thanks for the message level hint :-)

  Are you sure? yes | no

Jonathan Beri wrote 01/20/2017 at 16:48 point

Are you thinking about a PCA9685-based shield as well?

  Are you sure? yes | no

Radomir Dopieralski wrote 01/20/2017 at 17:16 point

No, I used those with Feather HUZZAH, and I'm not impressed. They only have 16 channels, the I2C registers are a bit complex (enough to need a library for it) and they are actually for driving LEDs, not servos. With a simple atmega I have everything they would give me, plus 4 more channels, and it's cheaper.

  Are you sure? yes | no

Jonathan Beri wrote 01/20/2017 at 18:47 point

I've used it pretty happily in other servo projects but those are fair points. Are you able to achieve better resolution and/or faster frequency with the atmega? 

  Are you sure? yes | no

Radomir Dopieralski wrote 01/20/2017 at 18:53 point

The frequency I'm using is fixed at 50Hz, because that's what the servos want. I can do 1µs resolution at that frequency, which with my servos translates to 0.01° angle (except for a corner case that I just found where two servos are in really similar positions -- then it's more like 5µs).

Don't get me wrong, the PCA9685 is a great piece of silicon and I would use it -- if I didn't write the code to do it without it first. For Tote there is an extra advantage -- I only need one chip for both the robot behavior and the PWM -- though I will be moving away from that anyways.

  Are you sure? yes | no

esot.eric wrote 01/23/2017 at 01:22 point

so, bitbanged PWM on 20 channels with 1us resolution? and presumably serial of some sort? Impressive!

  Are you sure? yes | no

Radomir Dopieralski wrote 01/23/2017 at 11:16 point

Well... I found recently a bug where if two channels are too close to each other, one of them wouldn't work -- so I had to modify it, so that you only have 1µs resolution when all the channels are at least 10µs apart from each other. When two or more get too close, they get merged into the same signal. So in the worst case, where all the channels are within 10µs of the first one, you only get 10µs resolution.

The i2c communication happens without interrupts, so fortunately it doesn't affect the timers.

  Are you sure? yes | no

Radomir Dopieralski wrote 01/20/2017 at 18:54 point

By the way, making a PCA9685-based shield should be extremely straightforward -- it would basically be just all the connections.

  Are you sure? yes | no

Radomir Dopieralski wrote 01/20/2017 at 20:50 point

Hmm, it's even more straightforward than I thought:


  Are you sure? yes | no

Mike Causer wrote 10/19/2016 at 01:17 point

Time to put my stash of SG90s to good use.

  Are you sure? yes | no

Craig Hissett wrote 09/14/2016 at 17:00 point

I need at least two of these in my life!

Awesome work matey!

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

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