Clarification on SPI Encoders

I am finding conflicting information about how to use the ODrive with an SPI encoder.

I am using an AS5047P-TS_EK_AB.

I have followed the instructions on this page and am getting errors.

The way it’s written (ie, directly there in the main docs with no word to the contrary) leads me to believe that this is expected to work with the default firmware.

However, for me it doesn’t.

On looking around in the forums I have seen a few people saying that SPI doesn’t work out of the box and you need custom firmware.

Some people imply that I need a specific branch; others are saying that I used to but now the changes have been merged into devel.

For all I know, both those statements are out of date and the devel branch has since been merged into master - in which case I have a totally separate problem to debug and I can rule out the firmware as the source of the issue.

So can someone who knows tell me, what firmware do I need to use the AS5047P? Is it a custom branch, is it devel, or is it all now in master?

As a sidenote:

My Odrive is brand new and running firmware version v0.5.1-dev. If I run “odrivetool dfu” it tells me that this firmware is newer than the most up to date version it can find online (v0.5.1).

So I have not flashed anything and have continued with the out-of-the-box firmware.

On Github, neither devel or master branches refer to v0.5.1-dev. Is my v0.5.1-dev already using the devel branch, or is that just a naming thing?

Following up with more specifics about the problem I am seeing:

Basically I have followed the SPI instructions to the letter, apart from using GPIO pin 3 instead of 4, and setting the mode to ENCODER_MODE_SPI_ABS_AMS instead of ENCODER_MODE_SPI_ABS_CUI.

After the save_configuration() and reboot() calls, I have also power cycled the ODrive. (I found a post saying you had to do that to allow the encoder itself to reboot.)

The encoder is has pins 3.3V and GND connected to the 3.3V and GND pins of J3 on the ODrive.

At this point, I am finding that the ODrive is always in an error state. This is the output from dump_errors(odrv0):

In [97]: dump_errors(odrv0,True)
axis0
  axis: Error(s):
    AXIS_ERROR_ENCODER_FAILED
  motor: no error
  fet_thermistor: no error
  motor_thermistor: no error
  encoder: Error(s):
    ENCODER_ERROR_ABS_SPI_COM_FAIL
  controller: no error
axis1
  axis: no error
  motor: no error
  fet_thermistor: no error
  motor_thermistor: no error
  encoder: no error
  controller: no error

This error does not go away if you just clear errors on start up, the way the docs page suggests it might.

What values do you have for encoder.spi_error_rate and encoder.pos_in_cpr ?

I have personally been having a lot of problems with the AS5047P since early 2020 - I can’t tie it down to the ODrive hardware, software, or the chips themselves. It’s quite bizarre. The same wiring that used to work perfectly in the past doesn’t, even if I shorten it to a few cm.
The chips seem to be setting their internal error bit, with no explanation as to why - are we polling them too fast? No, I tried slowing that down to no effect. Are they damaged? No, I have ordered new chips and they behave the same.
In certain motor positions they seem to work - try putting both pos_in_cpr and spi_error_rate on the liveplotter and turning the motor very carefully by hand. Again I have no idea why they would do this.

I’ve all but given up on them, and will go for an alternative from MPS or CUI in future.

spi_error_rate is 0.9997616410255432.

Is this likely to be a comms and/or noise issue then? I have followed the docs exactly, meaning the 3.3V is wired to the 3.3V, the GND is wired to the GND. The encoder is running entirely from the ODrive because I understood that would help avoid ground loops.

Should I have tied any other pins to any other values? Or done anything else to mitigate noise?

pos_in_cpr doesn’t exist, I assume “pos_cpr” is its replacement. The value of pos_cpr is fixed at 0.1249847412109375 no matter how much I hand-turn the motor.

What’s interesting is that spi_error_rate is also fixed. It stays identical, even to all those decimal places.

I tried turning the ODrive off and on again, and just measuring the spi_error_rate on the screen. It starts at around 0.918 and over the course of several seconds comes back to the exact same value 0.9997616410255432, from which it then never moves.

I tried it again, this time running liveplotter as soon as I turned it on. It gives a very clear graph:

Graph of spi_error_rate from switch on:
spi_error_rate

Does this phenomenon mean anything to anyone better acquainted with the ODrive firmware?

For that dev kit, the manual indicates that you need to set it for 3.3V operation in hardware: https://www.mouser.com/datasheet/2/588/AS5047P-TS_EK_AB_Operation-Manual_Rev.1.0-775823.pdf

Looks like the jumper JP1 has to be set for 3.3V and the jumper (0 ohm resistor) R1 has to be desoldered and moved to R2.

1 Like

Or feed it 5V instead, of course.

Thanks for pointing that out. I have moved R1 to R2 and left the wiring as it is.

As a result I now have one of two problems:

  • with JP1 on the left, I still see ENCODER_ERROR_ABS_SPI_COM_FAIL as soon as I turn on the ODrive (and persisting even after I clear the error a few times.) spi_error_rate floats around 0.5, and pos_cpr appears to fluctuate randomly even though I am not moving the motor at all.

  • with JP1 on the right (where I believe it’s supposed to be), I see no errors but all readings are exactly 0. That includes spi_error_rate, pos_cpr and everything else in odrv0.axis0.encoder. Despite claiming no errors, the ODrive does not respond when I do odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE.

  • with JP1 removed entirely, I see the same symptoms as with JP1 on the left.

This makes me think JP1 should be on the right, but do I need to “turn it on” somehow?

I see ‘is_ready’ in .encoder (which is False) but nothing in .encoder.config that implies it will enable, activate or turn on the encoder.

The jumper jp1 is fairly pointless - it just connects the power input to either the 5v pin or the 3.3v pin on the header. You can just as easily desolder the jumper and connect either 5v or 3.3v directly to the middle pad.
(AS5047P-TS-EK-AB schematic - page 9)
However wetmelon is right, you need to move some resistors for it to work at 3.3v.

See also this thread: AS5047P-TS_EK_AB SPI 3V3 soldering needed!

The garbage output and SPI_Error_Rate ~= 0.5 is exactly the behaviour I get, running on 5V.

It used to work fine on the end of a 2 metre cable, no issues whatsoever. Two motors both at 30A from a 50V supply.
Then after a few months it started to get SPI errors when the motor was running at a reasonable torque with 50V supply, and these errors went away if I lowered the supply voltage to 25V.
These days, it doesn’t work at all, with exactly the same hardware, firmware, etc. I have tried changing the firmware, the ODrive, the wiring, and the sensor board itself all to no avail. It’s weird. :frowning:

My understanding is that the high error rate and nonsense readings are happening when I’m not actually powering the chip. JP1 is tying the centre pin to the 5V on the header, which is not connected.

When I place JP1 on the right, it’s tying the centre pin to 3.3V, which is connected to the ODrive.

That is when the errors vanish and I get a nice set of very neat and very useless zeroes when I print the .encoder object.

Right sorry - I misread your post.
If you’re getting readings with the chip unpowered, then the ODrive is clearly ‘trying’ to read it. There’s nothing else you should need to do to turn the chip ‘on’, other than to power it.

Nothing happens at all when you move the motor by hand? There are no positions in which you get a non-zero reading?

Moving the motor by hand makes no difference.

This is what I see from odrivetool:

In [4]: odrv0.axis0.encoder
Out[4]:
error = 0x0000 (int)
is_ready = False (bool)
index_found = False (bool)
shadow_count = 0 (int)
count_in_cpr = 0 (int)
interpolation = 0.0 (float)
phase = 0.0 (float)
pos_estimate = 0.0 (float)
pos_estimate_counts = 0.0 (float)
pos_cpr = 0.0 (float)
pos_cpr_counts = 0.0 (float)
pos_circular = 0.0 (float)
hall_state = 0 (int)
vel_estimate = 0.0 (float)
vel_estimate_counts = 0.0 (float)
calib_scan_response = 0.0 (float)
pos_abs = 0 (int)
spi_error_rate = 0.0 (float)
config:
  mode = 257 (int)
  use_index = False (bool)
  find_idx_on_lockin_only = False (bool)
  abs_spi_cs_gpio_pin = 3 (int)
  zero_count_on_find_idx = True (bool)
  cpr = 16384 (int)
  offset = 0 (int)
  pre_calibrated = False (bool)
  offset_float = 0.0 (float)
  enable_phase_interpolation = True (bool)
  bandwidth = 1000.0 (float)
  calib_range = 0.019999999552965164 (float)
  calib_scan_distance = 50.26548385620117 (float)
  calib_scan_omega = 12.566370964050293 (float)
  idx_search_unidirectional = False (bool)
  ignore_illegal_hall_state = False (bool)
  sincos_gpio_pin_sin = 3 (int)
  sincos_gpio_pin_cos = 4 (int)
set_linear_count(count: int)

I’m afraid I have very little intuition about what to do next here.

What version of firmware are you using? nm you said

I assume that’s from github.com/odriverobotics/ODrive?
Have you tried the devel branch?

Also I assume you have saved config and rebooted?
Only other thing I could suggest is getting hold of an oscilloscope and checking what’s really going on with the SPI

I’m not sure whether I’m on the devel branch or not - that was actually one of the things I was initially asking about.

I don’t know where 0.5.1-dev comes from, and I’m not keen on overwriting it to then find I can’t get it back again. Without knowing where I stand on the firmware front, I’m not sure what I can rule it out as the cause of the problems.

I was hoping one of the devs would step in and clarify the situation, but I don’t know how closely they watch this forum.

And yes, I have saved and rebooted many, many times now.

So, I take it you have never compiled the firmware from source yourself?
0.5.1-dev /was/ a branch, it was closed when firmware 0.5.1 was released. You have some pre-release version of 0.5.1 but i’m not sure if its possible to tell exactly which. Is this the version that came shipped with the board?
If you auto-update now, (odrivetool dfu with no parameters) I think you will get the released 0.5.1 version.
It would also be worthwhile for you to clone the Git repo and check that you can build the current devel (even if you don’t flash it) this is the very latest but is changing all the time.

When I try running odrivetool dfu, it warns me that to proceed would be a step backwards since the version I have is more recent than the latest in the Git repo. So I haven’t gone ahead.

I would like to have a go at compiling the firmware for myself, since I (very optimistically) was thinking about playing around making some changes in there, once I’d got a handle on the more basic operation.

But I am very far away from that level of competence right now and it’s not going to help my current situation.

EDIT: I’ve cloned the ODrive repo and am looking through the commit logs now. The first reference to SPI I can see is from 31st December 2020, where they change something to do with a GPIO pin’s configuration. There is an earlier SPI-related fix on the 22nd December. Unfortunately I have no idea whether this info is relevant or not.

We made a mistake when we released 0.5.1, I built it from 1 commit beyond the tag and it got labeled “dev”. So it’s probably the 0.5.1 release code.

@jbombastor People have all sorts of problems with this dev board, I suggest doing a forum search because this problem has been solved a dozen times over

@Wetmelon How do you even do a forum search on here? I see no search bar anywhere. The “all categories” selector box gives you a search box that only seems to work with category headings. Previously on creating New Post it would try and find similar posts for me, but I’ve just tried it now and it either doesn’t come up or produces unhelpful results.

((EDIT: worked it out. Once it was dark outside, my monitor was bright enough for me to see the dim search icon in the top right corner. I can’t stop and look now but I will come back and see if it throws up any results that weren’t on Google.))

I had already read every post from here that Google would show me before posting. Most of what I found didn’t apply to the symptoms I’m getting, and most of the posts I read did not end in the problem being solved.

As a newcomer who doesn’t yet have the experience or know all the jargon, it’s quite possible I could have missed some important details because I didn’t know what to look for.

But I’m sure even I would have noticed if my problem had been solved a dozen times over.

If there is some resource I’ve missed, can anyone tell me where it is?

And, if this board is such a troublemaker, can the devs recommend one they know will actually work? Or are they all like this?

I assume from your response that the firmware on my ODrive is probably identical to the latest release, and so it’s safe to dfu update it. I’ll also assume that the ODrive can probably be trusted and the problem is with the encoder board.

1 Like

A few more findings, posted here for completeness:

One chap posted on a different post that you need to tie the TEST pin to ground. (I don’t understand why you would, and there was no explanation given.) But I did that anyway, and now I get this result, which is subtly different from what I had before:

In [31]: odrv0.axis0.encoder
Out[31]:
error = 0x0000 (int)
is_ready = False (bool)
index_found = False (bool)
shadow_count = 0 (int)
count_in_cpr = 0 (int)
interpolation = 0.5 (float)
phase = 0.0013422966003417969 (float)
pos_estimate = 0.0 (float)
pos_estimate_counts = 0.0 (float)
pos_cpr = 0.0 (float)
pos_cpr_counts = 0.0 (float)
pos_circular = 0.0 (float)
hall_state = 7 (int)
vel_estimate = 0.0 (float)
vel_estimate_counts = 0.0 (float)
calib_scan_response = 0.0 (float)
pos_abs = 0 (int)
spi_error_rate = 0.0 (float)
config:
  mode = 257 (int)
  use_index = False (bool)
  find_idx_on_lockin_only = False (bool)
  abs_spi_cs_gpio_pin = 3 (int)
  zero_count_on_find_idx = True (bool)
  cpr = 16384 (int)
  offset = 0 (int)
  pre_calibrated = False (bool)
  offset_float = 0.0 (float)
  enable_phase_interpolation = True (bool)
  bandwidth = 1000.0 (float)
  calib_range = 0.019999999552965164 (float)
  calib_scan_distance = 50.26548385620117 (float)
  calib_scan_omega = 12.566370964050293 (float)
  idx_search_unidirectional = False (bool)
  ignore_illegal_hall_state = False (bool)
  sincos_gpio_pin_sin = 3 (int)
  sincos_gpio_pin_cos = 4 (int)
set_linear_count(count: int)

So we now have a hall_state, an interpolation and a phase. Although none of them do anything and pos_cpr still stays at 0 when I move the motor by hand.

However, now it actually lets me run odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE

Unfortunately the result is an immediate ENCODER_ERROR_ABS_SPI_COM_FAIL. But still, it’s a result to report.

After that I tried to swap back to ABI mode, which never worked either. That didn’t work and gave me the error message ENCODER_ERROR_NO_RESPONSE when I ran AXIS_STATE_FULL_CALIBRATION_SEQUENCE.

I’m now going to wire the encoder board up to an arduino and see if I can see the ABI outputs changing onscreen when I move the motor by hand.

Well that’s answered one question. On wiring it up to the arduino, all three outputs (ABI) are high all the time, and the chip quickly gets hot to the touch. I guess it’s fucked somehow and I’ll have to buy another one.

I have no idea what would have caused that, but the first ODrive I bought did the same thing.

If it’s not throwing that error when IDLE but throws it when trying to calibrate, you have the classic SPI noise error, which is good because you’re making progress! Use short (< 6") SPI lines, use 50 ohm series resistors, shielded cable, or better yet - use shielded twisted pair over a differential bus (you have to add RS422 transceivers at both ends). Also, use the ferrite rings (available here) Personally, I don’t recommend SPI without differential, as it’s just too much trouble, unless you have EXTREMELY short wires.

I will be testing a differential encoder solution this coming week, so you may be able to get a known working solution directly from us shortly.