001
002package jmri.jmrix.pi;
003
004import com.pi4j.io.gpio.GpioController;
005import com.pi4j.io.gpio.GpioFactory;
006import com.pi4j.io.gpio.GpioPinDigitalInput;
007import com.pi4j.io.gpio.Pin;
008import com.pi4j.io.gpio.PinPullResistance;
009import com.pi4j.io.gpio.PinState;
010import com.pi4j.io.gpio.RaspiPin;
011import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
012import com.pi4j.io.gpio.event.GpioPinListenerDigital;
013
014import jmri.Sensor;
015import jmri.implementation.AbstractSensor;
016import jmri.jmrix.pi.simulator.GpioSimulator;
017
018import org.slf4j.Logger;
019import org.slf4j.LoggerFactory;
020
021/**
022 * Sensor interface for RaspberryPi GPIO pins.
023 *
024 * @author   Paul Bender Copyright (C) 2003-2017
025 */
026public class RaspberryPiSensor extends AbstractSensor implements GpioPinListenerDigital {
027
028    private static GpioController gpio = null;
029    private GpioPinDigitalInput pin = null;
030    private PinPullResistance pull = PinPullResistance.PULL_DOWN;
031
032    public RaspberryPiSensor(String systemName, String userName) {
033        super(systemName, userName);
034        // default pull is Pull Down
035        init(systemName, PinPullResistance.PULL_DOWN);
036    }
037
038    public RaspberryPiSensor(String systemName, String userName, PinPullResistance p) {
039        super(systemName, userName);
040        init(systemName, p);
041    }
042
043    public RaspberryPiSensor(String systemName) {
044        super(systemName);
045        init(systemName, PinPullResistance.PULL_DOWN);
046    }
047
048    public RaspberryPiSensor(String systemName, PinPullResistance p) {
049        super(systemName);
050        init(systemName, p);
051    }
052
053    /**
054     * Common initialization for all constructors.
055     * <p>
056     * Compare {@link RaspberryPiTurnout}
057     */
058    private void init(String systemName, PinPullResistance pRes){
059        log.debug("Provisioning sensor {}", systemName);
060        if (gpio == null) {
061            if (!RaspberryPiAdapter.isSimulator()) {
062                gpio = GpioFactory.getInstance();
063            } else {
064                gpio = GpioSimulator.getInstance();
065            }
066        }
067        pull = pRes;
068        int address = Integer.parseInt(systemName.substring(systemName.lastIndexOf("S") + 1));
069        String pinName = "GPIO " + address;
070        Pin p = RaspiPin.getPinByName(pinName);
071        if (p != null) {
072            try {
073                pin = gpio.provisionDigitalInputPin(p, getSystemName(), pull);
074            } catch (java.lang.RuntimeException re) {
075                log.error("Provisioning sensor {} failed with: {}", systemName, re.getMessage());
076                throw new IllegalArgumentException(re.getMessage());
077            }
078            if (pin != null) {
079                pin.setShutdownOptions(true, PinState.LOW, PinPullResistance.OFF);
080                pin.addListener(this);
081                requestUpdateFromLayout(); // set state to match current value.
082            } else {
083                String msg = Bundle.getMessage("ProvisioningFailed", pinName, getSystemName());
084                log.error(msg);
085                throw new IllegalArgumentException(msg);
086            }
087        } else {
088            String msg = Bundle.getMessage("PinNameNotValid", pinName, systemName);
089            log.error(msg);
090            throw new IllegalArgumentException(msg);
091        }
092    }
093
094    /**
095     * Request an update on status by sending an Instruction to the Pi.
096     */
097    @Override
098    public void requestUpdateFromLayout() {
099       if (pin.isHigh())
100          setOwnState(Sensor.ACTIVE);
101       else setOwnState(Sensor.INACTIVE);
102    }
103
104    @Override
105    public void dispose() {
106        try {
107            gpio.unprovisionPin(pin);
108            // will remove all listeners and triggers from pin and remove it from the <GpioPin> pins list in _gpio
109        } catch ( com.pi4j.io.gpio.exception.GpioPinNotProvisionedException npe ){
110            log.trace("Pin not provisioned, was this sensor already disposed?");
111        }
112        super.dispose();
113    }
114
115    @Override
116    public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event){
117       // log pin state change
118       log.debug("GPIO PIN STATE CHANGE: {} = {}", event.getPin(), event.getState());
119       if (event.getPin() == pin){
120          if (event.getState().isHigh()) {
121             setOwnState(!getInverted() ? Sensor.ACTIVE : Sensor.INACTIVE);
122          } else {
123             setOwnState(!getInverted() ? Sensor.INACTIVE : Sensor.ACTIVE);
124          }
125       }
126    }
127
128    /**
129     * Set the pull resistance on the pin.
130     *
131     * @param pr The new PinPullResistance value to set.
132     */
133    private void setPullState(PinPullResistance pr){
134        pull = pr;
135        pin.setPullResistance(pull);
136    }
137
138    /**
139     * Set the pull resistance.
140     * <p>
141     * In this default implementation, the input value is ignored.
142     *
143     * @param r PullResistance value to use.
144     */
145    @Override
146    public void setPullResistance(PullResistance r){
147       if (r == PullResistance.PULL_DOWN) {
148          setPullState(PinPullResistance.PULL_DOWN);
149       } else if(r == PullResistance.PULL_UP ) {
150          setPullState(PinPullResistance.PULL_UP);
151       } else {
152          setPullState(PinPullResistance.OFF);
153       }
154    }
155
156    /**
157     * Get the pull resistance
158     *
159     * @return the currently set PullResistance value. In this default
160     * implementation, PullResistance.PULL_OFF is always returned.
161     */
162    @Override
163    public PullResistance getPullResistance(){
164       if (pull == PinPullResistance.PULL_DOWN) {
165          return PullResistance.PULL_DOWN;
166       } else if(pull == PinPullResistance.PULL_UP) {
167          return PullResistance.PULL_UP;
168       } else {
169          return PullResistance.PULL_OFF;
170       }
171    }
172
173    private final static Logger log = LoggerFactory.getLogger(RaspberryPiSensor.class);
174
175}