Problem
init() clears the counter, and nothing public can set it afterwards. So the reported angle is always relative to wherever the shaft rested at power-up, and it changes on every boot.
This blocks a natural setup: an MT6835 (driver is in this repo) gives an absolute position over SPI, while its ABZ output gives fast hardware counting. But the ABZ half cannot be absolute, so anything saved in encoder coordinates — an end-stop range, a zero_electric_angle — is wrong after a restart. The zero_electric_angle error is multiplied by pole_pairs.
angleOverflow and angleCounter are protected, and only the index-pin ISR resets them, which needs a Z channel.
Checked against current master.
Suggested fix
A public method to place the counter at a known angle:
void ESP32HWEncoder::setAbsoluteAngle(float angle)
{
if(!initialized){return;}
int32_t count = (int32_t) lroundf(angle * cpr / _2PI);
count %= cpr;
if (count < 0){ count += cpr; }
taskENTER_CRITICAL(&spinlock);
pcnt_counter_clear(pcnt_config.unit);
angleOverflow = count;
angleCounter = 0;
angleSum = count;
taskEXIT_CRITICAL(&spinlock);
}
A setCounter(int32_t) in raw counts would work just as well.
One detail worth building in
Wrap the result into [0, 2PI). Sensor::update() treats a jump above 0.8 * 2PI as a full turn, so an unwrapped offset corrupts the full-turn counter.
A related problem that seeding does NOT solve
Worth stating explicitly, because it looks like part of this request but isn't.
getAngle() is full_rotations * 2PI + angle. full_rotations only counts crossings of the 0/2PI wrap point since boot, so it always starts at 0 wherever the shaft happens to rest. Seeding the counter fixes the within-turn part; it does nothing about the turn count. If the shaft boots on the far side of the wrap point from where a position was recorded, getAngle() is still a full turn off.
The correct fix is what single-turn absolute drives do: at startup, don't trust the turn counter — reconcile it against the stored reference and pick the whole turn that puts the shaft nearest it. Unambiguous for any axis travelling under half a turn, works from any resting position, and leaves full_rotations free to wrap normally during operation, so velocity is unaffected.
That belongs on Sensor, not here — it applies to every single-turn sensor. Something like a Sensor::addFullRotations(int32_t) (shifting vel_full_rotations too, so the correction isn't read as motion) would be enough. Happy to raise it separately against Arduino-FOC.
Workaround without changing the library
getSensorAngle() is public and virtual, so a subclass can store the offset once and add it back on every read.
Happy to open a PR — radians or raw counts?
Problem
init()clears the counter, and nothing public can set it afterwards. So the reported angle is always relative to wherever the shaft rested at power-up, and it changes on every boot.This blocks a natural setup: an MT6835 (driver is in this repo) gives an absolute position over SPI, while its ABZ output gives fast hardware counting. But the ABZ half cannot be absolute, so anything saved in encoder coordinates — an end-stop range, a
zero_electric_angle— is wrong after a restart. Thezero_electric_angleerror is multiplied bypole_pairs.angleOverflowandangleCounterareprotected, and only the index-pin ISR resets them, which needs a Z channel.Checked against current
master.Suggested fix
A public method to place the counter at a known angle:
A
setCounter(int32_t)in raw counts would work just as well.One detail worth building in
Wrap the result into [0, 2PI).
Sensor::update()treats a jump above0.8 * 2PIas a full turn, so an unwrapped offset corrupts the full-turn counter.A related problem that seeding does NOT solve
Worth stating explicitly, because it looks like part of this request but isn't.
getAngle()isfull_rotations * 2PI + angle.full_rotationsonly counts crossings of the 0/2PI wrap point since boot, so it always starts at 0 wherever the shaft happens to rest. Seeding the counter fixes the within-turn part; it does nothing about the turn count. If the shaft boots on the far side of the wrap point from where a position was recorded,getAngle()is still a full turn off.The correct fix is what single-turn absolute drives do: at startup, don't trust the turn counter — reconcile it against the stored reference and pick the whole turn that puts the shaft nearest it. Unambiguous for any axis travelling under half a turn, works from any resting position, and leaves
full_rotationsfree to wrap normally during operation, so velocity is unaffected.That belongs on
Sensor, not here — it applies to every single-turn sensor. Something like aSensor::addFullRotations(int32_t)(shiftingvel_full_rotationstoo, so the correction isn't read as motion) would be enough. Happy to raise it separately against Arduino-FOC.Workaround without changing the library
getSensorAngle()is public and virtual, so a subclass can store the offset once and add it back on every read.Happy to open a PR — radians or raw counts?