End switches an interrupts


Great :clap: My switches are connected. I’ m ready for testing.
@Wetmelon Do you think one GPIO for both switches at one actuator is enough or do you prefere to use two seperate GPIOS for bottom and top position?

Greetings Zennix


You can probably just use one switch for home per actuator. Don’t really need two switches on each.

Simulator rig with ODrive

Hi Wetmelon,

I have seen you are working at this topic.
Task List

  • [ ] Add hardware support
  • [ ] Add callbacks
  • [ ] Homing speed limit
  • [ ] Homing function
  • [ ] Fault on endstop pressed flag

One question:
The last item in the list is for emergency. My actuators have two limit switches each. So one for home position (down limit) and the other for the upper limit. Could you please add an additional input to detect the upper limit too?
Otherwise I have to detect it externaly.

Thanks / Zennix


For reference, here is the task list.


Hi Zennix,

The software as written supports MIN and MAX endstops. So it’ll support both (2 per axis) if configured :slight_smile:



great :+1:
Is it ready for testing? Devel branch?

Greetings / Zennix

6DOF motion platform using ODrive

Hey, sorry about the delay. Please try this branch https://github.com/Wetmelon/ODrive/tree/Endstops

You’ll need to compile from scratch and upload with an STLink, unless there’s some way for you to use DFU to pull this pull request? https://github.com/madcowswe/ODrive/pull/218

Once you flash the controller, you can configure it normally, with the addition of the following endstop commands:

odrv#.axis#.config.max_endstop.gpio_num = <1, 2, 3, 4, 5>
odrv#.axis#.config.max_endstop.enabled = <True, False>
odrv#.axis#.config.max_endstop.offset = <int>
odrv#.axis#.config.max_endstop.is_active_high = <True, False>

odrv#.axis#.config.min_endstop.gpio_num = <1, 2, 3, 4, 5>
odrv#.axis#.config.min_endstop.enabled = <True, False>
odrv#.axis#.config.min_endstop.offset = <int>
odrv#.axis#.config.min_endstop.is_active_high = <True, False>

gpio_num and enabled should be self explanatory. Make sure to disable step/dir or UART communications as necessary.

offset can be set as a number of counts. It is used as the “home switch position”. In other words, if your min_endstop is 100 counts further than what you want 0 to be, set this value to -100. This is really only currently valid for min_endstop.

is_active_high sets the polarity of the endstop. If when the switch is pressed it pulls the pin LOW (this is very common for endstop switches), you want to set this to “False”.


Hi Wetmelon,

thanks for the branch. It compiles and the ODrive is running as expected.
My configuration is:

odrv#.axis#.config.max_endstop.gpio_num = 6
odrv#.axis#.config.max_endstop.enabled = True
odrv#.axis#.config.max_endstop.offset = -100
odrv#.axis#.config.max_endstop.is_active_high = False
odrv#.axis#.config.min_endstop.gpio_num = 5
odrv#.axis#.config.min_endstop.enabled = True
odrv#.axis#.config.min_endstop.offset = -100
odrv#.axis#.config.min_endstop.is_active_high = False

my switches are active low, but I have no external pullup.

Is there a command for homing the axis?
I tried to press a switch during a running position command, but nothing happend. The motor was still running.

Greetings / Zennix


Looking at the code from the PR, I believe you want to set requested_state to AXIS_STATE_HOMING to begin the process.


Please pull the newest version of the firmware, there was a bug where it wouldn’t initialize any GPIO above #5. The internal pull up / down is set automatically depending on the polarity of the endstops.

I’ve added <axis>.config.startup_homing = <True, False> as an option. If both <axis>.config.startup_closed_loop_control and <axis>.config.startup_homing are True, then ODrive will home the axis after calibration. Otherwise, you can enter homing by setting <axis>.requested_state = AXIS_STATE_HOMING

After setting up your configuration, please remember to do <odrv>.save_configuration() then power cycle the controller.


Hi Wetmelon,

Thank you.
I pulled the last version and here is my configuration

startup_motor_calibration = True (bool)
startup_encoder_index_search = True (bool)
startup_encoder_offset_calibration = True (bool)
startup_closed_loop_control = True (bool)
startup_sensorless_control = False (bool)
startup_homing = True (bool)
enable_step_dir = False (bool)
counts_per_step = 2.0 (float)
ramp_up_time = 0.4000000059604645 (float)
ramp_up_distance = 12.566370964050293 (float)
spin_up_current = 10.0 (float)
spin_up_acceleration = 400.0 (float)
spin_up_target_vel = 400.0 (float)
gpio_num = 5 (int)
enabled = True (bool)
offset = -100 (int)
is_active_high = False (bool)
gpio_num = 6 (int)
enabled = True (bool)
offset = -100 (int)
is_active_high = False (bool)

I set .config.startup_homing = True and .config.startup_closed_loop_control to True. Saved it and start. It does only the full calibration sequence. Nothing more.

The command .requested_state = AXIS_STATE_HOMING ends with:

In [11]: odrv0.axis0.requested_state = AXIS_STATE_HOMING
NameError Traceback (most recent call last)
d:\tools\Anaconda3\lib\site-packages\fibre\shell.py in ()
----> 1 odrv0.axis0.requested_state =
NameError: name ‘AXIS_STATE_HOMING’ is not defined

Is there a way to see if the switches are pressed?

Greetings / Zennix

How to use github?

Hi Wetmelon,

I tried the switches also at Pin 3 and 4. The same result. I pressed one of the switches and checked odrv.axis0.
I thought min_ or max_endstop_state should be true.

B [25]: odrv0.axis0
error = 0x0000 (int)
enable_step_dir = False (bool)
current_state = 1 (int)
requested_state = 0 (int)
loop_counter = 318529 (int)
min_endstop_state = False (bool)
max_endstop_state = False (bool)

Greetings / Zennix


Make sure you’re using the odrivetool that comes with the repository (v0.4.2.dev) rather than the one that comes from pip. That should fix the AXIS_STATE_HOMING issue. If you don’t want to do that, you can use .requested_state = 9

As for it not seeing the endstops… will investigate.

EDIT: I found a few issues… It will take me another day to clean up, but it’s definitely working (at least on GPIO 5). There’s still a bug where it immediately faults out because it goes into AXIS_STATE_CLOSED_LOOP_CONTROL while the homing switch is still pressed


Ok, the absolute newest build should work. I tested it on my bench and it seems to work correctly.

I recommend using a GPIO pin above #4 (so 5, 6, 7, or 8 on v3.5)


Hi Wetmelon,

looks good, thank you.

I tested GPIO 3, 4, 5 and 6, there are recognised.
Automatic homing is working too.
Reaching an endstop during a run is also working. It stops the motor.
manual homing is also working.

one finding:
I saw, that about every second try the homing function will not work. The motor initializes, searches home. If the min button is pressed the motor stops and will not go to it’s offset.
In [2]: odrv0.axis0.error
Out[2]: 2049

BTW: how to start the new ODrivetool in a Windows environment without installing it through pip?

Great work!!!

Greetings / Zennix



another finding:
endstop_max gpio 8 and endstop_min gpio 7 are not working correct. Config is showing correct, pressing the max switch, the axis will show the min switch is pressed. The min switch at 7 will not be recognised.

Greetings / Zennix


Error 2049 is 0x801, which is what happens when the min endstop is pressed while in “run” state. I suspect what’s happening is that it has insufficient debouncing so it moves through the entire set of states all in one press. I may be able to fix this in software, but one quick and easy way to fix it is to put a decoupling capacitor or little RC filter on the GPIO.

You can start the new ODrivetool by manually navigating to the ODrive/tools folder and calling ipython -i ./odrivetool

What happens if you set max_endstop.enabled = False?



OK, thank you. I will try to use a RC filter later on.

Here is the test with disabled max_endstop.

gpio_num = 7 (int)
enabled = True (bool)
offset = -10000 (int)
is_active_high = False (bool)
gpio_num = 8 (int)
enabled = False (bool)
offset = -100 (int)
is_active_high = False (bool)

Max_endstop switch is pressed:

In [9]: odrv0.axis0
error = 0x0801 (int)
enable_step_dir = False (bool)
current_state = 1 (int)
requested_state = 0 (int)
loop_counter = 175924 (int)
min_endstop_state = True (bool)
max_endstop_state = False (bool)

Greetings / Zennix


Hello and thanks for your sharing your own experiences. Would you mind sharing the technical information below?

Switch limit type
Is your limit switch wired to an arduino board and then the arduino is connected to the odrive?If so, can you share the wiring From -To pinouts?
Would you be able to show a video how switch works?
If you are using arduino, would you mind sharing the sketch?




I had a look to your code, but couldn’t find why the GPIO 7 and 8 isn’t working as expected.

But one idea to void Axis::set_min_endstop_enabled(bool enable) in axis.cpp
you set interrupt_mode = GPIO_MODE_IT_RISING_FALLING. For debouncing isn’t it better to set it like:

uint32_t interrupt_mode = config_.min_endstop.is_active_high ? GPIO_MODE_IT_RISING : GPIO_MODE_IT_FALLING;

You can use every micro switch that fits to your application. I used: https://www.ebay.de/itm/10-Stueck-AC-125V-5A-SPDT-1NO-1NC-Glatt-Hinge-Lever-Mini-Micro-Schalter-DE/282522069641?hash=item41c7a0aa89:g:kxcAAOSwCkZZPuy7

The connetion is very easy. It’s directly connected to a GPIO on the ODrive. The other contact is connected to Ground GND.

Greetings / Zennix