Lepton Deku mod

lol, it sure seems that way. This morning I installed STM32CubeIDE and generated the configuration as you described, and the result was slightly different than the one Valentine has since posted. Nonetheless they both produce 64MHz and work properly.

void SystemClock_Config(void)
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLN = 8;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  /** Initializes the CPU, AHB and APB buses clocks
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

I think we can conclude that mine is 16Mhz * 8 / 2 = 64MHz, and Valentine’s is 16MHz * 12 / 3 = 64MHz. PLLM is set to 1 in both cases so no effect, and PLLP and PLLQ are set to 2 but apparently don’t apply to the main clock.

So that’s the CPU speed done. Motor runs smooth one direction, rough the other.

Then I added this bit of code before the call to loopFOC, so the PWM change is done in response to the sensor change rather than the two updating on independent schedules:

  int8_t oldHallState = sensor.hall_state;
  static uint32_t lastUpdateTime = 0;
  // Wait for sensor change, but give up after 10ms
  while(sensor.hall_state == oldHallState && millis() < lastUpdateTime + 10) {}
  lastUpdateTime = millis();

That improved the consistency of the sensor change times a little bit in smooth running direction (intervals are now mostly in the area of 600us, varying from a little under 500 to a little over 700), but rough direction is still rough. Times vary from about 1000 to 2300us. However there is a pattern to it which repeats every 6 electrical revolutions, which is one full turn of the rotor. And it looks like the speed is pulsating. Adding up the average sensor state times, the average electrical revolution times are 9600, 8000, 8000, 7700, 7800, 9000.

It may be another mechanical problem, because my modified rotor isn’t perfectly balanced. The gap between the sensors and the rotor lip varies by about 0.1mm throughout the turn. But it is strange that it would only affect it in one direction.

1 Like

Awesome, glad it’s working out.

Correct, there are multiple solutions. The reason is that this allows you to fine tune the clocks of the various sub-systems of the MCU, as you found out by messing the UART timing.


Just out of curiosity I tried one of the old sensor placements again (green dots on the diagram), and whaddya know, it works! The usual preferred direction spins about 5% faster, but both are equally smooth and have similar scatter in the sensor intervals (around 630-830us and 660-860us).

But I do find it interesting that the cyan placement resulted in 20-25% higher top speed in the good direction (500-700us). I wonder if it would be better to stick with how I have it now, or try the other again and advance the electrical angle by 60 degrees when going the bad direction. Maybe it will smooth out and run fast too. But it may be performing some crude field weakening, getting that high speed at the cost of requiring more current to produce the same torque.

On to tuning velocity mode. First attempt with P=0.2, I=2, D=0 from the example vibrated horribly and wouldn’t stop. Second attempt with my “wait for sensor change” code removed (so it’s running motor.move/loopFOC at the highest possible rate now) wouldn’t move at all for a while, then vibrated for a while, then went back to not moving.

EDIT: Yep, I’m pretty sure yellow/green is the correct sensor placement and cyan is field weakening in one direction. I tried converting those microsecond times into RPM, and got this (I’m running at 2 volts, so kv is RPM/2):
600us = 2778RPM = 1388kv
730us = 2283RPM = 1141kv
760us = 2193RPM = 1096kv

The motor was originally listed as 2300kv, and I rewound it from 29 turns delta to 36 turns wye, so it should theoretically be 2300 * 29 / 36 / sqrt(3) = 1070kv.

1 Like

@runger I’ve found a tricky bug.

float HallSensor::getVelocity(){
  if (pulse_diff == 0 || ((long)(_micros() - pulse_timestamp) > pulse_diff) ) { // last velocity isn't accurate if too old
    return 0;
  } else {
    return direction * (_2PI / (float)cpr) / (pulse_diff / 1000000.0f);

Sometimes it returns infinity due to divide by zero, because pulse_diff is volatile and can be changed by interrupt. It needs to be cached in a local variable before checking if it’s equal to zero.

Thanks for finding this!
I’ve made a ticket: HallSensor.cpp volatile accèss bug · Issue #244 · simplefoc/Arduino-FOC · GitHub
and we’ll solve it for the next release…

While researching online, I did find companies that make bus bars suitable for automated assembly. None were cheap. Some can be soldered with paste in an oven and some used through holes.

Google “solderable bus bar” and you get lots of hits.

As for increasing current carrying ability by placing solder on traces. It does not work well. Generic solder has only on order of 10% the conductivity of copper. It can be as good as 40% if you use solder with a high silver content. But you likely want the lowest melting solder you can find for this

A simple solution, I have a vacuum tube based guitar amplifier made by Fender. It is a newer design that uses a PCB. Finder used a twisted pair of normal vinyl insulated wire for a high current path. It is loose with plenty of airspace under it.

Your solution seems ideal if you are making a few dozen PCBs,

I wanted what Tesla does? They make many thousands of cars with 100+ amp BLDC motor drivers.

The right solution for a few dozen boards will probably not be the right solution for thousands of boards. If you really are planning for ‘tesla’ scale you should probably start talks with a couple of PCB manufacturers and have your engineers hash out what each of them can do for your currents and at what price. About the wire solution as used in your amp, as far as I understand it (so check it with an actual engineer) these solder connection can degrade due to shock and vibe so it might no be the best solution for a robot.

I have also been thinking about high current capacity boards and what I can do within the limits of LCSC/JLCPCB (2oz copper). There is a nice thread at eevblog on high current capacity traces. I’m torn between soldering wire on top of the trace, getting plated slots and soldering in a ‘busbar’ (cut up some copper sheets myself) or just keeping the traces very short and having board to wire connections for each of the phases instead of using traces to distribute power.

With Tesla’s style of vertical integration, you could just make whatever size of bus bars you need in-house and load them on your pick-and-place machine :slight_smile:

One thing I’ve been thinking of trying is to replace those solder blobs with little copper blocks, maybe 1.5x1x3mm. I’m not sure if I could solder them with an iron, but probably could with low temp solder paste and a hot plate. I could even do the bars on the back at the same time, though I may have to give up the ceramic capacitors.

On the software side of things, I finally have angle control! 5 long years I’ve struggled with this. I feel like I need to write an academy award speech thanking everyone for your various contributions :stuck_out_tongue:

My velocity PID values are 0.1, 1.5, 0. Low-pass filter is 0.05, and angle P is 8. I still have a little more investigation to do because the voltage-based current limit doesn’t seem to be working, but that’s not essential.

One more trick I used is to restrict the target angle to cogging steps. Otherwise it jitters between them instead of settling.

target_angle = floor(target_angle / (_PI_3/motor.pole_pairs)) * (_PI_3/motor.pole_pairs);

Next I will try a 2208 motor that I already have set up with hall sensors which are probably in good locations, so hopefully it will be no trouble. That should allow testing up to 10-15 amps, and get a feel for how much the copper bars boost the current carrying capacity compared to one of the other boards without them.

EDIT: Turns out the current limit was working, I just wasn’t changing it on the fly properly. motor.current_limit only works before initialization, afterward you have to change motor.PID_velocity.limit. So now I can crank up the speed. I currently have it set to a fairly conservative current limit 1.5, voltage limit 4 and velocity limit 5000. I’ve also got it set up to skip calibration and initialize the sensor electric_rotations and electric_sector to the values I get when the servo tester is all the way to the left, so there’s no more jumping at startup. Now I can actually put this little linear actuator to use :slight_smile:


Did you have an angle sensor in this setup or only hall sensors? Because it should be able to settle at any position if you have an angle sensor.

My mechanical designs never have access to the end of the shaft for a magnetic angle sensor, so hall sensors are pretty much the only choice. Positioning is plenty precise as is, but I could still give linear halls a try for smoother motion and so I don’t have to solder all those tiny capacitors between the leads.

Here is the actuator in actual use :slight_smile: It works every bit as well as I’d hoped.




Magnetic sensors aren’t the only option and some of those options don’t need access to the end of the shaft, something like an AMT103 can fit an up to 8mm shaft going through it and other (more expensive) options can fit wider shafts. That seems unnecessary for your application though as the video shows it working very well in that amazing wing you made.

True, but AMT103 is bigger than the actuator :slight_smile:

I decided to give it a try soldering copper blocks below the MOSFET GND pins with an iron. I soaked up the old solder blobs as best I could with desoldering braid, but there was still enough left that it was difficult to get them in just the right place. After much heating and prodding with tweezers, they’re all half way decent. Paste and hot plate would be better.

1 Like

Those boards are tiny. They could potentially be used for PNP head actuator for rotating the part while the linear motion is pneumatic, air controlled, piston w. linear magnetic encoder. I remember back when I was engaged in the PNP community someone mentioned pneumatic linear control as a superior route. It still needs to rotate though, and the part has to be picked by vacuum, maybe even blown off, although the paste does grab the part. The part is analyzed by the up looking cámara w. Visual recognition for offset and rotation. It’s not safe to just rely on pick-up-coordinate and theoretical orientation, or maybe some parts like small Caps and resistors could work with a 0.1mm tolerance.

Pneumatic control is an entirely different ballgame, I know, I know. But perhaps PWM control is needed for that kind of piston/valve movement

Maybe we can 3d print the valve/piston housing and more or less just use basic coils with a well defined spring to counter the force (make it easier to control)

I’m afraid I have to Excalibur this a bit. Maybe it doesn’t need to be so complicated. What if, like when changing nozzle, the part could be rotated while being looked at by the up-looking cam. So the rotating mechanism/actuator would not need to be part of the head, but kind of like a robotic hand, nodging the part into the right angle. When rotated the cam then “sees” the offset, if any, and then moves on to the next part already picked (head w. eg. 10 linear pneumatic actuators). All controlled by SimpleFoc! :call_me_hand:

And OpenPnP of course…

Sometimes ideas like these has to be digested through some subconscious deep-dive (sleep). I think I’ve got it. So when the part has peen picked, and are ready for visual inspection by the up looking cam. The actuator plunges into a camera housing where the “entrance” is a soft maybe rubber BLDC roter. The actuator has a linear encoder attached so the movement is tightly controlled but still the “entrance” has some bounce/spring. So it’s rotating the part, if necessary, by friction while the actuator is resting on the soft rotor while the part is inside the housing for visual inspection. Tada :tada:

Large parts, like larger then the rotating entrance, would maybe need a small independent motor (stepper or bldc) but since 98% of the parts will fit inside the custom housing, only one of the linear actuators/PNP-nozzles will need it. Compared to 10 motors on the head this makes a huge difference.

This entails having two up-looking cameras, but that is actually not such a bad idea, because the camera inside the “custom housing” can have a close look at smaller parts (fixed focal length) and the other up-cam can “see” the larger parts in its view. Remember 0402 are really tiny. Having the same camera with fixed focal length look at 0402 caps and large MCUs may cause some detail issues or said in other words, if the cam has a close look at 0402 the visual recognition is smoother.

Maybe it does not need to be friction, but large “gears” which would need some clever code to match up when “docking”

1 Like

About this bug… it has been merged on the dev branch, so if you want you could check it out and see if it improves things for you…

No success with the 2208 motor yet. As with the small motor the sensor gap was too large to get reliable readings, so I ground the rotor lip flush with the magnets and reduced the gap to 0.5mm. Spins perfectly open loop, but closed loop just buzzes in place. Odd since it did spin poorly before. Maybe the missed sensor changes were what allowed it to move forward. I’ll keep fiddling with it.

In more exciting news, I was at Micro Center the other day and spotted a syringe of tin-bismuth solder paste. I decided to give it a try for soldering all the copper bits, and this is the result.

I’d say next time I should be a bit more liberal with the paste, but nonetheless a satisfactory result for a first try (after a small test on scrap PCB).

I hammered the copper wire into nearly square cross section this time so it’s the same height as the capacitors. This way the board could lay flat on its back while melting the solder on both sides at once. It worked, but making the bars taller results in them not being quite wide enough to cover all the vias. The top bar maybe also should be a bit longer to reach that third row of vias that comes up beside the MOSFET. The bottom one could actually be a bit shorter. The last set of vias it needs to reach can be seen right above the silkscreen “VCC”.

Applying paste to all the capacitors took a good while, so I’m not sure this is actually superior to how I did it before with a soldering gun. Much better for the tiny copper bits on the top side though. And it does make positioning the bars more precise and relaxing. Maybe next time I’ll just slop more paste on the bars and smush the capacitors into it rather than carefully applying it to each of them. Or solder one bar this way, and then do the capacitors and other bar with the gun (it’s easy once you have one bar tacked in place so you can push up against it, plus that would allow using wide/thin bars like before). Or ditch the ceramics and use more electrolytics.

To melt the solder, I placed it on a 2x2x0.5 inch block of aluminum I had lying around, put that on the stove, and lit it up. Low flame for a minute or two until the solder goes shiny, give it a few more seconds, and turn it off. Note the spare capacitor peeking under the edge which is being used as a spacer to make sure the board lays flat on the copper bars and other capacitors. Pay no attention to the mess of food crumbs in the burner tray :stuck_out_tongue:


The 2208 is running now, although still not quite right. Turns out I had a poorly soldered sensor wire. Here’s a diagram showing the sensor placement.

As with the 9N12P diagram back in post #27, the rotor is positioned as I imagine it would be with red and blue coils energized. Green dots are the actual sensor locations, yellow dots are an equivalent placement, and cyan dots are opposite timing (in this case offsetting by one stator arm does work). Note that the yellow/green dot is half way between magnets, implying that each trapezoid120 sector holds near a sensor transition point, while one cyan dot is half way between magnets, implying that sectors hold strongly at the center of a sensor state. I’ve verified this to be true on the actual motor.

Like the small motor, cyan placement results in fast smooth spinning one direction and slow rough spinning the other, while the yellow/green placement results in slightly slower but still smooth spinning in the good direction. However this time the bad direction is still pretty bad even with the yellow/green placement.

Another peculiar thing is that swapping which motor wires are connected to which MOSFETs changes the quality a bit, with one particular combination working best. That was true on the small motor too, and I wondered if it was just a fluke, but apparently not. I’m pretty sure I tried a different Lepton on the small motor and got the same result, verifying that it wasn’t some damage done in the process of soldering the heavy copper bars.

But one good thing is that this motor turns very smoothly in open loop mode with SinePWM. No visible cogging. So I can set up a test to plot the time intervals between sensor changes and what would be sector changes if it were in trapezoid mode. Ideally green/yellow placement should have the sensor state change exactly half way between sector changes, while cyan should have sensor and sector change simultaneously. We shall see how far the intervals deviate from the ideal…

1 Like

After much procrastination in the form of working on other projects, I’ve finally collected the necessary data. The result is that the cyan placement performs as expected, changing close to the same time as the sectors would change in trapezoid mode (not perfect, but within ±10 electrical degrees of what they should be). But the yellow/green placement (which is what I need) does not work. The state changes are about 12 degrees offset from the sector changes (in addition to the ±10 scatter), whereas they should be 30 degrees offset.

Upon close examination of the motor, it does looks like the middle sensor isn’t exactly centered between the stator arms in green position, whereas it is when in cyan position. So it appears to be a machining error when drilling the first keyway hole. But I don’t think I’ve edited the CNC program since I ran it, and they are clearly 30 degrees apart, which is one stator arm. No idea what happened.

There is a technique I’ve been wanting to try, extrapolating the velocity to estimate the exact rotor position for SinePWM rather than using trapezoid modulation. If that works, I should be able to run with cyan placement. Otherwise I’ll have to try to fill and re-drill the keyway in the right place.

I have also noticed this on some of my motors (not this board). If I have a board with noisy motor and a board with a quiet motor, the noisy motor will be quiet on the other board and vice versa. I am not sure what qualities of the electronics cause this. If you discover any more info about this I would love to hear an explanation!

Time to get back to my main thread after my adventures in sensor extrapolation SmoothingSensor: Eperimental sensor angle extrapoltion

A major discovery made along the way is that if you use sinePWM instead of trapezoid120 and manually adjust motor.zero_electric_angle, you can correct for any phase offset of the hall sensors. As long as they’re placed 120 electrical degrees from eachother, it doesn’t matter how they’re placed relative to the stator. Both the imperfect yellow/green placement and perfectly out of phase cyan work equally well now. This is great because it means you don’t need precision machining of custom stator mounts to get correct timing.

The tuning procedure is to run in torque or velocity mode and command the motor to spin faster than it’s able to for the voltage/current limit, and adjust zero_electric_angle until the maximum speed reached is the same in both directions.

In my case it appears to be due to slight variations in sensor timing even though my stator mounts are precisely machined. I tried 3 different wire connection combos with the cyan sensor placement, and two of them were just about perfect if I offset zero_electric_angle by pi/7 from the value that the auto-calibration came up with, but one needed a little more offset. All of them reach about the same top speed and sound the same when the offset is right.

I made another discovery while playing with a hoverboard motor (by which I mean hacking out the center of the stator with a coping saw like a barbarian while dreaming of machining a big cycloidal reducer for it to flap a 7 foot long version of the wing in post #49), which is that it doesn’t have any capacitors on its hall sensor PCB. So I checked the sensors and they are marked 41F, which looks to be a digital hall that doesn’t need filtering capacitors. That will save a lot of fiddly soldering work.