2
votes

I have a misunderstanding about a certain Clojure/Java interop.

I'm using the Pi4J library and I want to get a listener on a GPIO Pin.

The usage documentation: https://pi4j.com/1.2/usage.html (under "Listen for Pin Changes") gives the following example:

public static class GpioUsageExampleListener implements GpioPinListenerDigital {
    @Override
    public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
        // display pin state on console
        System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = "
                + event.getState());
    }
}

// create and register gpio pin listener
myButton.addListener(new GpioUsageExampleListener());

There is also an example application: https://pi4j.com/1.2/example/listener.html

Which has:

myButton.addListener(new GpioPinListenerDigital() {
    @Override
    public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
        // display pin state on console
        System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = " + event.getState());
    }
});

So, I'm trying the following bit of Clojure code:

(import [com.pi4j.io.gpio GpioFactory GpioPinDigitalInput PinPullResistance RaspiPin]
        [com.pi4j.io.gpio.event GpioPinListenerDigital])

(def gpio (GpioFactory/getInstance))
(def mybutton (.provisionDigitalInputPin gpio RaspiPin/GPIO_02 PinPullResistance/PULL_DOWN))

(def gpio-listener (reify GpioPinListenerDigital
                    (handleGpioPinDigitalStateChangeEvent [this event]
                      (println "GPIO Event Occured"))))

(.addListener mybutton gpio-listener)

But I get:

No matching method addListener found taking 1 args for class com.pi4j.io.gpio.impl.GpioPinImpl

Using reflection, I have a look at the methods available to the mybutton instance, and I can see the method is there:

(clojure.reflect/reflect mybutton)

 :members
 #{{ 
    ;;; Stuff trimmed
   {:name addListener,
    :return-type void,
    :declaring-class com.pi4j.io.gpio.impl.GpioPinImpl,
    :parameter-types [java.util.List],
    :exception-types [],
    :flags #{:public :synchronized}}
   {:name addListener,
    :return-type void,
    :declaring-class com.pi4j.io.gpio.impl.GpioPinImpl,
    :parameter-types [com.pi4j.io.gpio.event.GpioPinListener<>],
    :exception-types [],
    :flags #{:varargs :public :synchronized}}}}

I've checked the type of gpio-listener against GpioPinListener with success.

(cast com.pi4j.io.gpio.event.GpioPinListener gpio-listener)
#object[user$reify__7404 0x11251bd "user$reify__7404@11251bd"]

I tried type hinting the parameter to GpioPinListener, but I get the same result.

My lack of Clojure and Java knowledge is failing me here, so I'm a little lost where to look next, but I'm sure it's something really basic.

1

1 Answers

3
votes

I can't test this, but if you note the docs, you'll see that addListener is actually var-arg, and dealing with var-arg methods can be confusing due to misunderstandings of how var-arg methods in Java work .

addListener is expecting an array of listeners (which is sugared away by Java var-arg syntax). Try

(.addListener mybutton
              (into-array [gpio-listener])) 

Or, since addListener actually has a List overload, this may work too

(.addListener myButton [gpio-listener])