So I started to work on the new PCB, using a QFN48 foorprint for the SAMD51G. Of course the footprint I made earlier for the IS31FL3733 chip was inexplicably broken — as soon as I started editing the pin descriptions it started doing really weird stuff. So I re-did it, together with a proper schematic image and all that. Then I started with the PCB I re-did recently for the #D1 Mini Tote, only replaced the PCA9685 with the SAMD51G:
Yes, it's a mess for now. Ideally, I will use the same headers as for the D1 Mini, so that I will be able to use all the D1 Mini shields with this robot — including the #Accelerometer Shield for D1 Mini I made recently (that's one reason why I removed the accelerometer from the PCB, actually). However, I still don't know which combination of pins will have the correct set of timers for making all those PWM outputs. But I'm confident that such a set of pins exists, because of an experiment I made with an ItsyBitsy M4:
import board import pulseio pins = [ 'D7', 'A1', 'D0', 'D1', 'D2', 'D4', 'D5', 'D9', 'SCL', 'D10', 'D11', 'D12', 'D13', ] pwms =  for name in pins: pin = getattr(board, name) print(name) pwms.append(pulseio.PWMOut(pin, frequency=50))
So yeah, I can have at least 13 PWM outputs — I'm sure I can find a set of pins that will work. Whether they will be easy to route is another question, but I have a lot of space and as many vias, as I need. Also note, that on the current PCB I actually have 16 servo sockets, not 12 — ultimately there will be 4 fewer.
I will probably also add a flash chip and that speaker with an amp — this way I can put all the turret voices from Portal on it.
I compiled a custom CircuitPython firmware with all the pins exposed, and ran a simple program looking for any combination of pins that would give me 12 PWM outputs:
for permutation in permutations(pins): pwms =  for pin in permutation: try: pwms.append(pulseio.PWMOut(pin, frequency=50)) except (RuntimeError, ValueError): pass if len(pwms) > 10: print(pwms) for pwm in pwms: pwm.deinit()
Yes, I know, I could have checked the datasheet for timers instead, but this is easier and makes more more certain.
That's a lot of permutations to check, so it took some time, but ultimately it came up empty. There is no permutation that would give me more than 10 PWM outputs on the SAMD21E18A chip. So what other options do I have now?
I could use a SAMD21G18A that has 38 GPIO pins — I'm sure they come with some extra timers that would enable a few extra PWMs. But the SAMD21E18A costs $2.38 in singles, while SAMD21G18A costs $3.47. That increase in price wouldn't be that bad by itself, however, for $4.01 I can have a SAMD51G19A instead, and a PCA9685 costs a dollar.
So now I have several options:
But it's not just about the price, is it? The SAMD51 is a massively more powerful microcontroller, with *much* more RAM, faster clock and more flash. Also hardware floating point operations and a bunch of other stuff. And that is for just $2 more. I really think that it's the best option. Besides, I've been wanting to make a board with that microcontroller for a while, but since #µGame Turbo is on hold, I didn't really have a chance. On top of that, I have one already in my drawer, so no need to wait for parts (though I will still need to wait for the PCB).
It turns out there have been some bugs in CircuitPython's pulseio module, related to how it selected what timer/channel to use for PWM for each of the pins. Long story short, it would sometimes re-use the channels that were already in use, resulting in those pins sharing duty cycle — you change it on one of them, it changes on the other as well. Obviously that's not good for us. The good news is that the bugs have been all fixed now. The bad news is that it's not possible anymore to have a PWMOut created for all the pins I'm using for servos at once — it was possible only because of that bug...
That means I need to redesign the PCB to use a set of 12 pins for the servos that can have PWMOut created for them all at once. To do that, I will need to do more testing to determine which pins I can use, and there is always a possibility that I don't find such pins — in which case I will need to switch to a bigger package, and possibly also a SAMD51 chip.
I also need to rethink the battery charging circuit — I want to keep the robot off while it's charging. Would also be nice to add that flash memory chip, for extra filesystem space for all the files.
I think I will drop the accelerometer in the process, because it turns out to not be that useful. I will also give up on the idea of keeping the top of the PCB clean and fitting all the components inside the hole in the battery holder — that was an interesting, but ultimately pointless challenge. I still want to have all SMD parts on one side of the board, though, because that makes assembly easier and cheaper, so they will probably all go on the top, with only THT parts on the bottom.
I had a bit of a break with this project, while I worked on other projects and dealt with some life stuff, but I very much still want to work on it. Obviously the very next step that needs to be done is to program the robot to do actual walking, at least at the level of #Tote, and then possibly make use of the accelerometer and the leg switches. I assume that this will actually take me some time. In the mean time, CircuitPython is being developed further and ported to the NRF52 chip, growing bluetooth support along the way, so the long-term plan is to switch to NRF52 from the SAMD21 chip. That will probably involve a redesign of the extension connector as well, so I don't think I will be working on any extensions until that happens.
Once the connector design is finalized, it will be time to look at possible extensions and the sensors to include on them. I want to have an OpenMV adapter, and also a simple "face" with distance sensors on it.
Since there is now at least one person trying to build this robot, I'd better post some build instructions. I don't have a full-blown material with photos yet, but I will try to make it workable.
So, first, what you will need:
To assemble the legs, first hot-glue four pairs of servos together to form the hips. You will need two "left" and two "right" pairs. The whole leg goes something like this:
And two of them will be a mirror image of that. Don't put in the screws that hold the servo horns, you will need to adjust the servo positions first.
Connect the servos to the PCB. There are helpful markings on it, such as "FR1" (front right 1) and "HL3" (hind left 3). The number goes 1 for ankle, 2 for knee and 3 for hip. The ground pin is marked, the middle pin is power, and the last pin is signal. You can use male pin headers and plug the servos in, or cut the cables and solder them directly. I prefer the latter, because then you can adjust the wire length and have better cable management. Make sure to leave some extra cable length for the leg to be able to move freely.
Once you have that, you can connect the robot to USB and create a main.py file with the following contents:
import board import pulseio import time _PINS = ( board.SERVO_HL1, board.SERVO_HL2, board.SERVO_HL3, board.SERVO_FR1, board.SERVO_FR2, board.SERVO_FR3, board.SERVO_FL1, board.SERVO_FL2, board.SERVO_FL3, board.SERVO_HR1, board.SERVO_HR2, board.SERVO_HR3, ) servos = tuple( pulseio.PWMOut(pin, frequency=75) for pin in _PINS ) for servo in servos: time.sleep(0.25) servo.duty_cycle = 6300 while True: time.sleep(1)
That will set all the servos to their middle positions. Disconnect the robot, insert the battery (mind the polarization) and switch the robot on. Now remove each servo from its horn and insert it back, making sure it's at a right angle to the horn. Secure the horns with the screws.
That gives you a finished physical robot.
I still didn't write the programs and libraries for it — I will be writing updates here when I do.
The PCBs arrived, and I have soldered them up:
I also rounded the corners of the switches a little bit, so that they better touch the ground.
One of the advantages of having the legs made out of PCB is that you can easily put things on them. Like, for example, switches that detect when the leg is touching the ground. I didn't include that in the original design, because I was a bit in a hurry, but that's not a problem, since I can make a separate set of legs easily.
It's a bit problematic to find the right kind of switch for this. For one, it has to be edge-mounted, but more importantly, it has to require very little strength. Regular "tactswitch" buttons are useless for that, but if you look around carefully, you will find something that is labeled "card detection switch" — those switches are supposed to detect when something, such as an SD card, is inserted into a slot. As such, they are rather sensitive, requiring very little force to be pressed. And they are usually in the correct orientation.
One such switch is the Panasonic ESE22MH27 that I happen to have in my drawer. A quick search for images reveals this footprint image:
So I quickly made a PCB with the footprint, and added some prototyping area to the leg, for good measure:
One annoyance is that I need 4 of those, but OSHPark makes them in batches of 3, so I need to order 6. At least I will have some extras to break.
I assembled a second PCB, this time without first breaking off all the bits of the legs, to see how it will look and feel. I actually had to break off one piece, to get to the USB port.
However, if I'm going to sell those, I think I will break all the bits off, and put it all in a bag. It will be smaller that way, and less fragile.
I still can't decide whether I want to ship with the battery holder and servo headers soldered or not. Having them soldered allows people to more easily assemble it, but makes the package thicker, and makes cable management tricky. If the headers are not soldered, they can cut the servo cables to the right length and solder them directly into the board, like I did with the first prototype:
On the other hand, since the battery holder has its cross-section roughly square anyways, the package has to be pretty thick to fit it no matter if it's soldered or not.
The PCBs from DirtyPCBs arrived today — pretty fast, I have to admit!
I got one of them assembled. The microcontroller worked first time, but I had to do several resolderings before I got the accelerometer to work. Seems like one of the pins in my gerbers is weird — probably another Fritzing bug. But no that I know it's there, I can scrap it a bit before soldering and it should be fine.
As you can see, everything fits neatly inside the hole of the battery holder, and around it. I decided to use a large, good quality speaker this time — maybe I can have it say the turret lines from Portal. Next up are the legs. Unfortunately that part still requires the use of hot glue.
I got the first four servos connected — since I don't want too long cables, I decided to cut them and solder them directly into the PCB, but you can as well solder male headers in there and plug the servos in. There is room for that.
I compiled a version of CircuitPython for Trinket M0 with adjusted pins, and the servos work nicely! Now I need to connect the remaining servos, and port the IK code I have to this.
Turns out that my simple yet brilliant idea for using the remaining board space for the mechanical parts of the legs is not so great after all. Most of the board houses noticed that people cram multiple designs into their boards, and started to demand extra payment for them. For example, at jlcpcb, you pay $2 for the first order, and $8 for every additional design on the same board, while ordering a separate PCB with that other design would cost you $5... Crazy stuff.
I decided that I won't be participating in that, and simply went to DirtyPCBs, who don't do such a thing. But they use white silkscreen with the yellow boards, which would look bad with the diagonal stripes that I added, so I ended up ordering black PCBs. Oh well.
Ultimately, I probably want to laser-cut the mechanical parts anyways, but I really wanted to test how this would work.