Sunday, July 14, 2013

Adding Home/Limit Switches

Home switches are especially convenient on 3D printers to return the printer to a known location, to get perfect adhesion from a perfect first layer height.  They're pretty much a requirement for delta 3D printers as well.

This post will walk through the steps to modify the base MachineKit image with LinuxCNC to add a limit switch.  Walking through the steps will take some time, but helps to explain concepts like the HAL and Device Tree.

Getting the Code

In a commit from 7/14, Charles checked in code to export and set up the limit switch inputs, in the MachineKit branch, so to get most of the code needed, do:

cd linuxcnc/src
git pull
sudo make setuid

... then follow the instructions and notes below to modify BeBoPr.ini and BeBoPr.hal for your specific printer.

BeBoPr Limit Switches

The BeBoPr board has 6 switch inputs which can be used as limit or home switches.

Our goal is to wire up one input switch to LinuxCNC so that when the X axis starts to home, pressing the NC (inverted-logic) switch causes the axis to stop and detect home.  To do this, we'll modify a bunch of files.

We'll use X-Max, the limit switch closest to the PWM outputs at the edge of the BeBoPr.  According to the BeBoPr manual, the BeagleBone black manual (BBB_SRM.pdf pg 80), and tracing the board traces, the X-max input switch is pin 32 on connector 8 and gpio0.11.


First, LinuxCNC needs to know that you want to use a limit switch when homing an axis.  The .ini file sets all kinds of config specific to your board and machine.

Setting an axis instance with a homing search velocity causes it go in the specified direction when you click the home button.  Here's a change that enables this:

diff --git a/configs/BeagleBone/BeBoPr/BeBoPr.ini b/configs/BeagleBone/BeBoPr/BeBoPr.ini
index f3b92a5..2bb64f7 100755
--- a/configs/BeagleBone/BeBoPr/BeBoPr.ini
+++ b/configs/BeagleBone/BeBoPr/BeBoPr.ini
@@ -165,8 +165,8 @@ MIN_FERROR = 0.25

 HOME =                  0.000
 HOME_OFFSET =           0.00
-#HOME_SEARCH_VEL =       0.10
-#HOME_LATCH_VEL =        -0.01
+HOME_SEARCH_VEL =       -10.0
+HOME_LATCH_VEL =        10.0

For more details on homing, see Sec 4, Homing Configuration, in:

You'll definitely want to make sure that the polarity of HOME_SEARCH_VEL is the right direction for your machine to drive it toward the home axis.

Now when you click the Home button in the LCNC GUI, you'll see the axis move.  Great!  But it has no way to find home, so it'll keep searching forever.


The HAL in LinuxCNC is the Hardware Abstraction Layer.  It provides a higher-level way to wire up in input to outputs, without having to recompile the LinuxCNC base code.

We also need to wire up a signal in the HAL so that the limit switch pin is connected to the axis homing logic.  home-sw-in is a pre-defined attribute of the axis instance that is meant for this:

diff --git a/configs/BeagleBone/BeBoPr/BeBoPr.hal b/configs/BeagleBone/BeBoPr/BeBoPr.hal
index 2bc5b38..1d9de6f 100755
--- a/configs/BeagleBone/BeBoPr/BeBoPr.hal
+++ b/configs/BeagleBone/BeBoPr/BeBoPr.hal
@@ -91,6 +91,10 @@ setp [PRUCONF](DRIVER).stepgen.00.steppin         0xA2
 # P8.44 PRU1.out4
 setp [PRUCONF](DRIVER).stepgen.00.dirpin          0xA3

+# Example from
+# Connect X-max input to X min input.
+net home-x => axis.0.home-sw-in
+setp 1

 # ################
 # Y [1] Axis

However, this change won't be enough, as the pin is unknown to the HAL. Running this code will cause LCNC to exit:

BeBoPr.hal:96: Pin '' does not exist
Shutting down and cleaning up LinuxCNC...

To get one step closer to making the pin accessible, modify this line at the top:

loadrt hal_bb_gpio output_pins=103,105,107,120,121,127,128,129,130,139,140,141,142,143,144,145,146

Add input_pins = 132 to the line.  Why 132?

From ~/linuxcnc/src/hal/drivers/hal_bb_gpio.c, line 191 has this comment:

Valid pins are 101-146 for P8 pins, 201-246 for P9 pins

You'll notice that every component seems to have its own numbering scheme.

Running this then yields:

linuxcnc@arm:~/linuxcnc/configs/BeagleBone/BeBoPr$ LINUXCNC - 2.6.0~pre
memmapped gpio port 0 to 0xb6f5c000, oe: 0xb6f5c134, set: 0xb6f5c194, clr: 0xb6f5c190
Waiting for component 'hal_bb_gpio' to become ready................................................................................................
BeBoPr.hal:25: /home/linuxcnc/linuxcnc/bin/rtapi_app exited without becoming ready
BeBoPr.hal:25: insmod failed, returned -1
Shutting down and cleaning up LinuxCNC...

The HAL can only provide access to a pin if the OS knows the pin.  Board-specific pin definitions are in the most recent version of Linux as the Device Tree.  We'll next add the missing pins to the device tree.

Device Tree for BeBoPr Cape

The file ~/linuxcnc/configs/BeagleBone/BeBoPr/BB-LCNC-BEBOPR-00A0.dts contains the device tree for the BeBoPr.  Here are some notes from Charles:

  • Every used I/O pin needs to show up in the dts device tree file, with an appropriate pin mux setting.
  • Any GPIO pin driven by the PRU (ie: Step, Dir, PWM) needs to be exported in
  • Direct PRU I/O pins do *NOT* need to be exported in
  • At least _one_ pin needs to be exported in to enable the low-level gpio hardware, or the hal_bb_gpio module will fail.  It is OK to export a pin used by hal_bb_gpio for is just not required that all hal_bb_gpio pins get exported
  • Any GPIO pins not driven by the PRU should be listed in the hal_bb_gpio command line.

The relevant section for the device tree is toward the bottom, and it configures attributes of pins.  The chip in the BeagleBone uses a big pin multiplexer for this:

        fragment@0 {
                target = <&am33xx_pinmux>;
                __overlay__ {
                        foo_pins: foo_pins {
                                pinctrl-single,pins = <
                                        0x18 0x0f       /* p8.3  gpio1_6        */
                                        0x08 0x0f       /* p8.5  gpio1_2        */

For each pin, there are two values:

First Value: The first value is the address offset of the pinmux register (or more specifically, the conf_<module>_<pin> register).  See the TRM section 9.3.51 for details, and table 9.10 for the addresses.  Note in the DTS file that the addresses are offsets from the start of the conf_ register section instead of the start of the control registers, so you have to subtract 0x800 from the listed address.

There's a nice spreadsheet on github that combines all the BeagleBone specific I/O stuff: will save you from having to cross correlate data from 3 or more manuals!  :)

Second Value: The second value is pin usage, which is also defined TRM section 9.3.51.  For example, pru1.r30.13 is to be used in mode 5 ("by the PRU"), and with bit 3 (pullup) disabled, bits 2-0 are 5, making this byte 0xd.  Other GPIO pins are mode 7, so the byte is 0xf.  Pin usage is also defined on page 98 of the BBB SRM.

Now we'll mod the device tree file. As a reminder, gpio0.11 is the X-max input switch, which is pin 32 on connector 8.

Page 80 of the SRM shows this pin to also be called gpmc_a19, lcd_data15, and UART5_RSTN.  Table 9-10 of the TRM shows offset 8DCh for lcd_data15; subtracting the 0x800 base, we get 0xdc for the first value.

The second value will be a bit different, too, because we want to set the RXACTIVE and PULLTYPESEL bits to 1.  These bits make the port an input and enable a pullup. Hence, the second value in the .dts for input pins should be 0x3f, not 0x0f.

diff --git a/configs/BeagleBone/BeBoPr/BB-LCNC-BEBOPR-00A0.dts b/configs/BeagleBone/Be
index 50f1709..67279d2 100644
--- a/configs/BeagleBone/BeBoPr/BB-LCNC-BEBOPR-00A0.dts
+++ b/configs/BeagleBone/BeBoPr/BB-LCNC-BEBOPR-00A0.dts
@@ -20,6 +20,7 @@
                "p8.28",        /* gpio2.24     Z_Ena   */
                "p8.29",        /* pru1.r30.9   Z_Dir   */
                "p8.30",        /* pru1.r30.11  E_Step  */
+               "p8.32",        /* gpio0.11     X_Max   */
                "p8.36",        /* gpio2.16     J4_PWM  */
                "P8.39",        /* pru1.r30.6   Y_Dir   */
                "P8.40",        /* gpio2.13     Y_Ena   */
@@ -36,6 +37,7 @@
+               "gpio0_11",
@@ -57,6 +59,7 @@
                                        0xe8 0x0d       /* p8.28 pru1.r30.10    */
                                        0xe4 0x0d       /* p8.29 pru1.r30.9     */
                                        0xec 0x0d       /* p8.30 pru1.r30.11    */
+                                       0xdc 0x3f       /* p8.32 gpio0_11       */
                                        0xc8 0x0f       /* p8.36 gpio2_16       */
                                        0xb8 0x0d       /* p8.39 pru1.r30.6     */
                                        0xbc 0x0f       /* p8.40 gpio2_13       */

After making these changes, run in the same dir (and possibly reboot):

cd ~/linuxcnc/configs/BeagleBone/BeBoPr/
sudo ./ looks like this:


dtc -O dtb -o BB-LCNC-BEBOPR-00A0.dtbo -b 0 -@ BB-LCNC-BEBOPR-00A0.dts && \
cp BB-LCNC-BEBOPR-00A0.dtbo /lib/firmware/

dtc -O dtb -o BB-LCNC-BEBOPRBR-00A0.dtbo -b 0 -@ BB-LCNC-BEBOPRBR-00A0.dts && \
cp BB-LCNC-BEBOPRBR-00A0.dtbo /lib/firmware/

It's invoking the device tree compiler to form a device tree file for the kernel to load.

There's one more place we need to make some mods.

The next file to mod is ~/linuxcnc/configs/BeagleBone/BeBoPr/  The bottom of this file looks like:

# Export GPIO pins
# This really only needs to be done to enable the low-level clocks for the GPIO
# modules.  There is probably a better way to do this...
while read PIN DIR JUNK ; do
        case "$PIN" in
                continue ;;
                [ -r /sys/class/gpio/gpio$PIN ] && continue
                sudo -A su -c "echo $PIN > /sys/class/gpio/export" || pin_err
                sudo -A su -c "echo $DIR > /sys/class/gpio/gpio$PIN/direction" || dir_err

done <<- EOF
        38      low     # gpio1.6       P8.3    Enable
        34      high    # gpio1.2       p8.5    Enable_n
        66      high    # gpio2.2       p8.7    Enable_n (ECO location)
        92      out     # gpio2.24      P8.28   Z_Ena
        80      out     # gpio2.16      P8.36   J4.PWM
        77      out     # gpio2.13      P8.40   Y_Ena
        74      out     # gpio2.10      P8.41   X_Ena

There are several different numbering schemes in use; the gpio numbers
in use the kernel pin numbering which is:

Kernel Pin = (<gpio_bank> * 32) + <gpio_pin>

Charles' pin assignments for the PRU are very similar, but off by 32 so that
a value of zero results in no pin being driven:

hal_pru_generic pin = ((<gpio_bank> + 1) * 32) + <gpio_pin>

...or for PRU direct pins:

hal_pru_generic pin = ((4 + 1) * 32) + <PRU_pin>

And you already read about about the hal_bb_gpio pin numbering based on the
P8/P9 pin numbers.

Here were my changes:

diff --git a/configs/BeagleBone/BeBoPr/ b/configs/BeagleBone/BeBoPr/
index 5a3843f..abf8e1a 100755
--- a/configs/BeagleBone/BeBoPr/
+++ b/configs/BeagleBone/BeBoPr/
@@ -54,6 +54,7 @@ while read PIN DIR JUNK ; do

 done <<- EOF
+        11      in      # gpio0.11      P9.32   X_Max
        38      low     # gpio1.6       P8.3    Enable
        34      high    # gpio1.2       p8.5    Enable_n
        66      high    # gpio2.2       p8.7    Enable_n (ECO location)

Now linuxcnc should start!  Almost there.

Testing It

First, verify that your limit switch pin is connected.  When you press it, the corresponding light on the BeBoPr should toggle.

Then, verify that Linux sees the same thing:

 sudo cat /sys/kernel/debug/gpio

You should see this:

GPIOs 0-31, gpio:
 gpio-11  (sysfs               ) in  hi

GPIOs 32-63, gpio:
 gpio-34  (sysfs               ) out lo
 gpio-38  (sysfs               ) out hi
 gpio-52  (eMMC_RSTn           ) out lo

GPIOs 64-95, gpio:
 gpio-66  (sysfs               ) out lo
 gpio-74  (sysfs               ) out hi
 gpio-77  (sysfs               ) out hi
 gpio-80  (sysfs               ) out lo
 gpio-92  (sysfs               ) out lo

Then press the switch, and the top line should go low:

GPIOs 0-31, gpio:
 gpio-11  (sysfs               ) in  lo

If that works, go to linuxcnc, toggle e-stop, toggle machine power, and then home the X axis.

Click 'Home X'.  The axis should move until the button is pressed; then it'll reverse until the button is released.

Woo!  You now have a working home switch.


  1. Brandon,

    How did you wire up the limit switch on the BeBoPr itself? I am trying to use a hall effect sensor instead of an actual switch and I wired it up so that when the sensor detects a magnetic field close it bias its internal transistor and brings pin 2 on the BeBoPr input for X_Max to low (shorts to ground) thus causing the Yellow LED to come on but when I open a terminal (LCNC is still running and active) the gpio pin still shows lo and never shows high?

    I am using the BBB with Bridge so for my setup the gpio is 2_5 (P8-09) not 0_11 like the White. I made the neccessary changes in the setup files and Charles already has the DTS setup correctly for this arrangment.. Any ideas? I tried it on all 3 MAX inputs with 3 seperate sensors and nothing happens when LED is on or off for that matter.


  2. [Just saw this - didn't get a ping.]

    I wired up the simple switch NC. X-max is in the corner by the PWMs; on this three-pin connector, I used the two pins farthest from the corner (or put another way, toward the reset switch).

    It sounds like you're wiring the right pin (2) and driving it low (which seems right, given the pull-up). Maybe verify with a simple switch that the X-max pin input works first, then test the hall effect sensor?

    Also, are you looking at the right gpio? 2_5 would be shown as gpio69 in /sys/kernel/debug/gpio.

  3. Hello, I have searched high and low for a diagram explaining what is what on the BeBoPr Cape. Which J8-14 is which, where do you hookup your Limit/Homing Switches.


    1. On my board, the reference designators are on the bottom of the PCB. The limit switches are the three-pin headers. Do you have the BeBoPr manual? If not, email me directly and I'll send you a copy.

    2. Hello Charles, I have seen the manual on the thread and know that the Limit Switches are located from J9 to J14.

      I also know that the GND/Neg/Pos are located as follows...

      What I don't know is which J? is for X/Y/Z/E? I didn't see that in the 2 page manual. Perhaps I don't have the right one?

      Thanks for your prompt reply.

    3. It's on page 10 of the manual. If you email me directly (or otherwise get me your email address) I can send you a copy.

      Limit switch (digital) inputs
      J9 – X-max limit sensor – BeagleBone gpio11
      J10 – X-min limit sensor – BeagleBone gpio10
      J11 – Y-max limit sensor – BeagleBone gpio9
      J12 – Y-min limit sensor – BeagleBone gpio8
      J13 – Z-max limit sensor – BeagleBone gpio78
      J14 – Z-min limit sensor – BeagleBone gpio79