# Programming the Lepton from start to finish

**URL:** https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935
**Category:** Uncategorized
**Created:** [February 5, 2023, 10:59pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935 "2023-02-05T22:59:46Z")
**Posts on this page:** 20
**Page:** 5

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 2, 2023, 8:56am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/81 "2023-03-02T08:56:35Z")

</div>

> [@Anthony\_Douglas](#):
>
> Where exactly is that code for the lepton yo ucommitted? I want to comb through it and compare my code to see if it sheds light on where mine is going wrong.

Here: [GitHub - simplefoc/simplefoc\_lepton\_platformio: Firmware for the SimpleFOC Lepton Board by Valentine](https://github.com/simplefoc/simplefoc_lepton_platformio)  
The code is in the `src/main.cpp` file.

> [@Anthony\_Douglas](#):
>
> From my stuff with stepper motors, you need more like 254 voltage levels to get a low noise sine wave. That’s what the so called silent stepper drivers use. 64 does not appear to be enough. In our case there might be more smoothing due to inductance or something, hopefully. Another good argument for the better of the two MCU candidates, then.

Yes, exactly… the different voltage levels you can set are the “vertical axis” of your sine-wave, and the “horizontal axis” is time, so determined by your loop iteration speed. Together you can see these things form a “grid” on which the controller can place the “samples” I was talking about earlier. So the whole thing is a discretization of the sine wave. The more samples you can place, the more accuracy you have in terms of voltage levels, the more faithfully you can re-produce the sine wave.

> [@Anthony\_Douglas](#):
>
> Maybe I can tweak things so it only updates the sensor a couple times per rotation, but updates the sine wave at higher frequency than that.

I would assume this is possible, you could design the control loop to use a “position estimator” instead of the sensor. The estimator would read the sensor at some frequency, but provide values to the control loop at a higher frequency, using some kind of interpolation logic in-between.  
But this will be a challenging implementation, and a hard pre-requisite to being able to do it will be to have a 100% working setup to start from. It’s impossible to do it without being able to verify what’s going on empirically.

---

<div class="post-metadata">

### Author: ![Anthony\_Douglas](https://community.simplefoc.com/user_avatar/community.simplefoc.com/anthony_douglas/32/2300_2.png) [@Anthony\_Douglas](https://community.simplefoc.com/u/Anthony_Douglas)
#### Post date: [March 2, 2023, 2:27pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/82 "2023-03-02T14:27:52Z")

</div>

Yeah, and it will take memory, too. That’s why I’m in a rush to upgrade to a better MCU. There are bound to be things I need to do, and even the most elementary program has to be absolutely squeezed into the lepton.

To be fair I am now having a similar problem with the blue pill. The flash is too small to run the commander. Once again it highlights the need for at least one flagship combo of hardware and code base that is reasonably flexible.

What we have right now is a bunch of parts lying around. Every single time I try any combination there are so, so many problems and barriers it’s ridiculous. It’s not a building block that can be stood upon.

I managed to get the open loop working with the bluepill, and the angle sensor. When I try the code I posted most previously, minimally adapted to make it fit the blue pil, it exhibits the same behavior, so now I at least know it’s not the lepton. I will follow Deku’s advice, but I think there is something the Commander does when it executes a command which I am not doing in my code, or something I am messing up.

Somehow, torque control mode is not being engaged, I think. It’s doing angle mode with some random angle, or something. I tried reloading the library in case, no dice.

That doesn’t explain the second weak spot, though. As I rotate the rotor, it has two weak spots. One stable point where the rotor started, and one about halfway around, which is not quite stable but pretty close. Like the space vector voltage curve… hm pretty sure that’s not related unless some wires got crossed in a remarkable coincidence of some kind.

Additionally, may we reflect for a moment perhaps, I’ve spend maybe 5 working days on this, = 200 bucks a day at least, so 1000 bucks, plus 240 for the boards and shipping, the frustration, and we aren’t done yet. At least $1240, at $3 per board, that’s a lot of boards to sell before you save that money. It wouldn’t solve all the problems here, but I’m just trying to point out that a couple extra bucks per board is a good deal if it makes things work better, even just in the development stage, even if you are making a lot of products that include said boards.

When I read the datasheets of other motor driver chips and boards, a lot of them hawk their “code free” stuff, precisely because it saves dev time. It’s a legit thing to save dev time, and people will accept higher per unit cost for sure. Esp when the per unit cost isn’t really the engineer’s problem.

---

<div class="post-metadata">

### Author: ![VIPQualityPost](https://community.simplefoc.com/user_avatar/community.simplefoc.com/vipqualitypost/32/4420_2.png) [@VIPQualityPost](https://community.simplefoc.com/u/VIPQualityPost)
#### Post date: [March 2, 2023, 4:08pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/83 "2023-03-02T16:08:56Z")

</div>

> [@Anthony\_Douglas](#):
>
> Commander does when it executes a command which I am not doing in my code, or something I am messing up.

The commander just executes whatever command you attached to it. For example:

```auto
void doTarget(char* cmd) { command.scalar(&motor.target, cmd); }
void doLimitCurrent(char* cmd) { command.scalar(&motor.current_limit, cmd); }

```

the setup in the beginning defines what command is attached to the commander function. What function are you running via commander that is not working in the configuration when you hardcode it?

> [@Anthony\_Douglas](#):
>
> I’m just trying to point out that a couple extra bucks per board is a good deal if it makes things work better, even just in the development stage,

I don’t think that anyone will argue that development is cheap. There is a saying: good, cheap, fast - pick two. I think if you want something developed quickly, and you want it to be good, you might be more interested in buying something like ODrive, which of course is at cost (and inspired this project to some point).

---

<div class="post-metadata">

### Author: ![Valentine](https://community.simplefoc.com/user_avatar/community.simplefoc.com/valentine/32/1024_2.png) [@Valentine](https://community.simplefoc.com/u/Valentine)
#### Post date: [March 2, 2023, 4:39pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/84 "2023-03-02T16:39:58Z")

</div>

> [@VIPQualityPost](#):
>
> here is a saying: good, cheap, fast - pick two.

The holy uncertainty principle trinity of all engineering projects:

1. Features
2. Cost
3. Time to market

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 2, 2023, 6:03pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/85 "2023-03-02T18:03:18Z")

</div>

> [@runger](#):
>
> I would assume this is possible, you could design the control loop to use a “position estimator” instead of the sensor. The estimator would read the sensor at some frequency, but provide values to the control loop at a higher frequency, using some kind of interpolation logic in-between.

As mentioned in the Lepton Deku Mod thread, I will be doing exactly this today. I was debating how to implement it just for digital hall sensors, but if it can be useful for other sensors like this I will make it generic. It was looking like it would have to hook in at the motor level anyway, since the estimation needs to be done using the filtered velocity.

---

<div class="post-metadata">

### Author: ![VIPQualityPost](https://community.simplefoc.com/user_avatar/community.simplefoc.com/vipqualitypost/32/4420_2.png) [@VIPQualityPost](https://community.simplefoc.com/u/VIPQualityPost)
#### Post date: [March 2, 2023, 7:16pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/86 "2023-03-02T19:16:29Z")

</div>

> [@dekutree64](#):
>
> debating how to implement

I wonder if you could implement a hardware RC filter to the ADC input in a way that for a certain range of speeds (filter frequency response), a single hall sensor will produce a smoothed analog voltage related to speed. Maybe the pull-up/down for the hall sensor also needs to be adjusted in that case.  
What approach were you thinking?

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 2, 2023, 8:10pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/87 "2023-03-02T20:10:12Z")

</div>

estimatedAngle = sensorAngle + velocity \* (currentTime - timeWhenSensorWasRead)

I think velocity will be the filtered value returned by FOCMotor::shaftVelocity(), sensorAngle will be the last unfiltered reading of the sensor, and then estimatedAngle will be fed into the angle low-pass in FOCMotor::shaftAngle(). But it’s possible the velocity will need to be a snapshot from the time the sensor was read, and sensorAngle will need to be a snapshot of the filtered shaft angle.

Hopefully tuning the filter time constants will be all that’s necessary to get it stable. I may also need to add a second order prediction, using velocity history to calculate acceleration, and multiply that by delta time squared.

It would be nice to have a test rig with both digital halls and a magnetic encoder to see how closely the estimate tracks the true position, and how it deviates during speed changes. Or on second thought, a magnetic encoder alone should do it. Read it at high frequency, but have a variable to control how often the prediction algorithm’s sensor value is updated. That way I can test the accuracy of the prediction over a wide range of sample rates.

EDIT: What the heck! First try it works perfectly 😆 Simple as adding this to BLDCMotor::move, just after the angle/velocity update:

```auto
  shaft_angle += shaft_velocity * (_micros() - sensor->angle_prev_ts);

```

So that is using the filtered angle and velocity to perform the prediction.

I edited the HallSensor class to use angle\_prev\_ts instead of its pulse\_diff variable, and override Sensor::update() to do nothing (the only useful thing it did was the full rotations check, which is now in HallSensor::updateState).

Sensor::angle\_prev\_ts is protected, so we’ll either need to make it public or add a get function for BLDCMotor to use it like that.

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 2, 2023, 11:37pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/88 "2023-03-02T23:37:03Z")

</div>

@dekutree64 this is kind of awesome!

Do you think it could be implemented as a Sensor class, like:

```auto
BLDCMotor motor = ...
SomeSensor wrappedSensor = ...
SmoothingSensor sensor = SmoothingSensor(&wrappedSensor, &motor);

void setup() {
...
wrappedSensor.init();
sensor.init();
motor.linkSensor(sensor);
...
}

```

The reason being that this way of doing it could work with any other sensor, and would not need modifications to the BLDCMotor class?

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 3, 2023, 12:02am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/89 "2023-03-03T00:02:00Z")

</div>

Only if the low-pass filtering is moved into the sensor class instead of the BLDCMotor shaftAngle and shaftVelocity functions. But that would involve modifying both BLDCMotor and FOCMotor…

I think it’s better to make it an option in BLDCMotor, as it really is only that one line then. Just a boolean to enable angle prediction, and perhaps a downsampling for the sensor-\>update() call in BLDCMotor::loopFOC so Anthony’s slow I2C sensor can be called less often.

I need to try tuning the velocity and angle PIDs for this motor and make sure it doesn’t cause any weird effects. Torque-voltage mode spins smoothly and equal speed in both directions, and responds well to my changing the speed by turning a servo tester knob. But high frequency torque/speed changes from PID may be another story.

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 3, 2023, 12:06am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/90 "2023-03-03T00:06:11Z")

</div>

But by providing the motor object to the SmoothingSensor class, you could access `motor->shaft_velocity` directly from the sensor? Or is that not enough?

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 3, 2023, 12:20am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/91 "2023-03-03T00:20:41Z")

</div>

If I’m not mistaken, that will cause an infinite recursion as sensor-\>getAngle() would call motor-\>shaftAngle() to get the filtered value for the prediction, and that calls sensor-\>getAngle().

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 3, 2023, 12:56am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/92 "2023-03-03T00:56:04Z")

</div>

I think motor will call `sensor.update()` on the SmoothingSensor, which will just read `motor->shaft_velocity` (not the function call but the field which contains the previously calculated, smoothed velocity) and call `wrappedSensor.update()` and `wrappedSensor->getAngle()` to get the new raw angle…

I like this better because it separates the position estimation code from the motor code, and works with any sensor.  
And it also means that it can be potentially composed with other wrapping sensors like the CalibratedSensor and others that we might come up with in the future…

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 3, 2023, 6:58am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/93 "2023-03-03T06:58:48Z")

</div>

Ok, here’s what I’ve got:

```auto
class SmoothingSensor : public Sensor
{
public:
  SmoothingSensor(Sensor *s, const FOCMotor *m):wrappedSensor(s),motor(m){}

  float getMechanicalAngle() { return wrappedSensor->getMechanicalAngle(); }
  double getPreciseAngle() { return wrappedSensor->getPreciseAngle(); }
  float getVelocity() { return wrappedSensor->getVelocity(); }
  int32_t getFullRotations() { return wrappedSensor->getFullRotations(); }
  int needsSearch() { return wrappedSensor->needsSearch(); }
  
  void update() {
    if(sensor_cnt++ < sensor_downsample) return;
    sensor_cnt = 0;
    wrappedSensor->update();
  }

  float getAngle() {
    return enable_smoothing == false ? wrappedSensor->getAngle() :
      wrappedSensor->getAngle() + (motor->shaft_velocity * _micros() - wrappedSensor->angle_prev_ts);
  }

  bool enable_smoothing = true;
  unsigned int sensor_downsample = 0; // parameter defining the ratio of downsampling for sensor update
  unsigned int sensor_cnt = 0; // counting variable for downsampling

protected:
  float getSensorAngle() { return wrappedSensor->getSensorAngle(); }
  void init() { wrappedSensor->init(); }
  
  Sensor *wrappedSensor;
  const FOCMotor *motor;
};

```

This class has to be declared a friend of Sensor so it can access wrappedSensor-\>angle\_prev\_ts and the protected functions.

It’s a bit different this way since it’s using the unfiltered getAngle for prediction and then filtering the result, but that may be more correct anyway, I’m not sure. Sensor::minDeltaT can’t be properly passed through to the wrapped sensor this way, but I suppose that’s not typically changed from the default anyway…

I need to set up a motor with a magnetic encoder to properly test it. My initial success may or may not have been due to the prediction algorithm. When I first changed from doing the prediction in BLDCMotor::move to this class, I accidentally left motor.linkSensor using the HallSensor directly rather than going through the SmoothingSensor, and yet it ran just fine. Previously this motor only ran well in one direction, so I am confused. Now it works with or without smoothing, and really I can’t tell much difference either way.

Tuning the voltage-based current limiting wasn’t much trouble. I ended up on 1400kv, which is a fair bit higher than the 990 that this motor should be, but I think is within typical range.

PID tuning is as fiddly as ever, and I never did get it quite right. I’ll try again tomorrow.

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 5, 2023, 5:50am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/94 "2023-03-05T05:50:36Z")

</div>

Never got time to work on it yesterday, but did manage to get a few minutes in earlier today.

It occurred to me the difference from before when it ran poorly in one direction is that now I’m using SinePWM instead of Trapezoid120, so maybe the low-pass filtering is already smoothing the shaft angle and allowing setPhaseVoltage to provide a smoother signal than trapezoid.

Velocity PID is now tuned, with values 0.01, 0.1, 0. Runs very smoothly and velocity tracks well.

I still can’t tell any difference at all whether SmoothingSensor.enable\_smoothing is enabled or not. I thought maybe wrappedSensor.angle\_prev\_ts was getting updated just before every prediction so the delta time was always basically zero, but my modified HallSensor class only updates it when a sensor changes state, so that’s not it. Although with other sensors that would be the case when sensor\_downsample is zero, since motor.loopFOC always calls sensor.update shortly before motor.move calls sensor.getAngle.

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 5, 2023, 10:35am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/95 "2023-03-05T10:35:29Z")

</div>

Does it maybe become more noticeable in some situations, like when turning very slowly?

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 5, 2023, 11:40pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/96 "2023-03-05T23:40:24Z")

</div>

Update on the Lepton testing: I’ve hooked up a motor, and tested different SPI sensors.

I could not make it work well with the SC60228 so far, although I did get the sensor working with the dev branch code (which has some bug-fixes compared to the release version) and by changing SPI Mode. Not sure what the problem is, TBH.

I then tested it with the MA330, and it seems to be working quite well. Tuning the PID without commander is a pain though. With the settings I have right now on an eMax GB4008 (66KV) turns well enough, draws hardly any power in velocity mode, and get it up to 75rad/s at 15V, that’s with about 3.5kHz loop speed…

More tomorrow.

---

<div class="post-metadata">

### Author: ![Anthony\_Douglas](https://community.simplefoc.com/user_avatar/community.simplefoc.com/anthony_douglas/32/2300_2.png) [@Anthony\_Douglas](https://community.simplefoc.com/u/Anthony_Douglas)
#### Post date: [March 6, 2023, 3:11am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/97 "2023-03-06T03:11:48Z")

</div>

Do you think Deku’s interpolator might help with loop speed? Sorry I haven’t gotten anything done recently… tomorrow I can experiment further to try to solve the baffling problem with the shaking stuff. My first stop will be a different sensor and magnet positioning approach, I bought an AS5048B. Or maybe I will try adjusting the distance of the magnet from the sensor… Or experiment to see if I can ascertain the cause of the variation between the pole pair check in the commander sketch and the pole pair check sketch… Probably switching out the sensor and magnet and magnet holder is the fastest way to get answers.

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 9, 2023, 4:24am UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/98 "2023-03-09T04:24:40Z")

</div>

Well no wonder it didn’t do anything, that code was total nonsense 😛 Hopefully this one is not so bad:

```auto
class SmoothingSensor : public Sensor
{
public:
  SmoothingSensor(Sensor *s, const FOCMotor *m):wrappedSensor(s),motor(m){}

  float getVelocity() { return wrappedSensor->getVelocity(); }
  int needsSearch() { return wrappedSensor->needsSearch(); }

  float getMechanicalAngle() {
    return enable_smoothing == false ? wrappedSensor->getMechanicalAngle() : _normalizeAngle(
      wrappedSensor->getMechanicalAngle() + motor->shaft_velocity * (_micros() - wrappedSensor->angle_prev_ts) / 1000000.0);
  }

  float getAngle() {
    return enable_smoothing == false ? wrappedSensor->getAngle() :
      wrappedSensor->getAngle() + motor->shaft_velocity * (_micros() - wrappedSensor->angle_prev_ts) / 1000000.0;
  }
  
  double getPreciseAngle() {
    return enable_smoothing == false ? wrappedSensor->getPreciseAngle() :
      wrappedSensor->getPreciseAngle() + motor->shaft_velocity * (_micros() - wrappedSensor->angle_prev_ts) / 1000000.0;
  }

  int32_t getFullRotations() {
    float angle = getAngle();
    return (angle - _normalizeAngle(angle)) / _2PI;
  }
  
  void update() {
    if(sensor_cnt++ < sensor_downsample) return;
    sensor_cnt = 0;
    wrappedSensor->update();
  }

  bool enable_smoothing = true;
  unsigned int sensor_downsample = 0; // parameter defining the ratio of downsampling for sensor update
  unsigned int sensor_cnt = 0; // counting variable for downsampling

protected:
  float getSensorAngle() { return wrappedSensor->getSensorAngle(); }
  void init() { wrappedSensor->init(); }
  
  Sensor *wrappedSensor;
  const FOCMotor *motor;
};

```

First of all, I had the parentheses in the wrong place in the getAngle calculation.  
Second, the delta time was in microseconds and needed to be divided by 1000000.  
Third, loopFOC uses electricalAngle, which uses sensor-\>getMechanicalAngle and thus was getting the un-smoothed value.

Since getAngle is ultimately only used in angle mode and I was testing in velocity mode, it really wasn’t doing anything at all.

It does seem to work now. It’s able to achieve about 25% higher speed, 150rad/s versus 120rad/s without smoothing. 2208 motor rewound from 13T delta 1450kv to 11T wye so theoretically 990kv. Voltage limit is set to 1, so speed should be 990\*2pi/60=103rad/s. 120 without smoothing implies 1147kv, 150 with smoothing implies 1433kv. When tuning voltage-based torque control without smoothing, I ended up on 1400kv giving the best behavior. I need to try measuring kv using the method of spinning it with a drill and measuring the AC voltage on the terminals and see how that compares.

It seems to need different PID tuning with smoothing turned on. This time with smoothing disabled I ended up on P0.1, I1.0, D0, and angle P20. That seems to give good behavior in velocity and angle mode. But with smoothing enabled, angle mode was a little choppy. P0.05, I0.1, D0, angle P5 seems to work well.

There is an audible difference between smoothing and not. Without smoothing, I can hear a sort of chirpy tone when the motor is spinning, one chirp for each hall state change. With smoothing, that goes away for the most part but I still hear it intermittently. And I can feel it in my hand holding the motor as well, slight skips in the smooth motion. Much less after the PID re-tuning, but still there. Without smoothing, the motion is consistent but always has that slight vibration of torque ripple due to hall sensor commutation.

EDIT: Measured back EMF with drill, 412mV versus 286mV for a different one that still has the original 1450kv winding. That means 1007kv, so pretty close to the calculated value. And since I have voltage\_power\_supply set to 7.4 but my battery is actually charged to 8.1V, that means I’m driving it at 1.095V, which should give 115.5rad/s. So the un-smoothed hall sensor commutation is just about spot on, and smoothing must be projecting the angle out ahead of where it should be and doing some field weakening.

---

<div class="post-metadata">

### Author: ![runger](https://community.simplefoc.com/user_avatar/community.simplefoc.com/runger/32/395_2.png) [@runger](https://community.simplefoc.com/u/runger)
#### Post date: [March 9, 2023, 7:47pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/99 "2023-03-09T19:47:03Z")

</div>

@dekutree64 , that’s awesome - can we add it to the drivers library? There’s already been interest in it as you know…

@Anthony_Douglas I’ve actually made board files for the Arduino IDE for the Lepton now.

 ![image](https://community.simplefoc.com/uploads/default/original/2X/7/70948ac467c5c4b1dcda38bb6ba160d963a02224.png)

 ![image](https://community.simplefoc.com/uploads/default/original/2X/2/21c0a43990c0578f1ba190aa6776c07f727a1870.jpeg)

This is compiling for me now.

The problem is, unlike with PlatformIO where I can share a project containing everything needed for a new board to github, and you can just download and use that project, for ArduinoIDE this stuff happens in the platform files.

So I’m actually unsure how to get it to you. Short of sending STM32duino a pull-request, them merging it, and you waiting for the next platform release, it seems that everything else is going to be some kind of kludge. ☹  
If anyone has any suggestions I’d love to hear them. I’ll also ask the stm32duino people if there is a method…

---

<div class="post-metadata">

### Author: ![dekutree64](https://community.simplefoc.com/user_avatar/community.simplefoc.com/dekutree64/32/3398_2.png) [@dekutree64](https://community.simplefoc.com/u/dekutree64)
#### Post date: [March 9, 2023, 9:47pm UTC](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935/100 "2023-03-09T21:47:55Z")

</div>

> [@runger](#):
>
> @dekutree64 , that’s awesome - can we add it to the drivers library? There’s already been interest in it as you know…

Certainly. But do include a note that it has had minimal testing. And you might change SmoothingSensor::getFullRotations() to just call wrappedSensor-\>getFullRotations() like I had it before. Technically getFullRotations()\*\_2PI+getMechanicalAngle() should give the same value as getAngle(), so I had to calculate it by the inverse of that incase the smoothing offset crosses the wraparound point. But I doubt anyone is actually using it in a way that it matters, and as it is I think it may be off by 1 sometimes due to the float to int conversion truncating rather than rounding.

And to update on the field weakening effect mentioned in my previous post, I think it’s specific to hall sensors. Each hall state change jumps 60 electrical degrees instantaneously, but the smoothed value will have gradually advanced through those degrees during the same time, meaning that on average it’s 30 electrical degrees ahead of where it should be.

EDIT: Also, SmoothingSensor won’t work with HallSensor at all as is. HallSensor needs to be edited to use angle\_prev\_ts instead of pulse\_timestamp. Give me a bit more time before you integrate it. I’ll see if I can come up with an elegant way to eliminate the field weakening effect…

EDIT2: I remembered we live in the future and the standard library has rounding functions now. So this should give fully correct results:

```auto
  int32_t getFullRotations() {
    float angle = getAngle();
    return lroundf((angle - _normalizeAngle(angle)) / _2PI);
  }

```

As for the other issue, one potential approach would be to add a phase advance setting in FOCMotor:

```auto
float FOCMotor::electricalAngle(){
  // if no sensor linked return previous value ( for open loop )
  if(!sensor) return electrical_angle;
  return _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() +
    phase_advance * _sign(shaft_velocity) - zero_electric_angle );
}

```

That will give user control of field weakening with any kind of sensor, as well as the ability to correct this particular problem. It’s fun to play with. 0.5 (~30 degrees) gives around 145rad/s with un-smoothed halls, so it looks like my theory was correct. 0.8 (~45 degrees) almost makes it to 160 rad/s 🙂

[Previous page](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935.md?page=4)

[Next page](https://community.simplefoc.com/t/programming-the-lepton-from-start-to-finish/2935.md?page=6)
