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